在实际开发中总会遇到很多异步问题。最常见的场景是一个接口请求后需要等待一段时间才能得到结果。如果遇到多个相互依赖的接口,那么问题就变得复杂了。大家总是在尝试用更好的方案来解决这些问题。一开始只能用回调函数,后来有人开始用Promise的思想来搞定。在ES6中,支持了原生的Promise,引入了Generator功能。直到ES7,使用async/await。这是一种用同步思维解决异步问题的方法。我想很多人可能分不清同步和异步的区别。如果你对事件循环理解透彻,那么你一定对异步的概念有很好的理解。当我们发出请求时,不会等待响应结果,而是继续执行后面的代码,响应结果的处理会在后续的事件循环中解决。那么同步就是说结果出来后代码还要继续执行。我们可以用一个两人问答的场景来对比一下异步和同步。A问B一个问题后,不等B回答,再问下一个问题,这是异步的。A问B问题后,他笑着等B回答。B回答完后,再问下一个问题。所以我们先记住这个特性,async/await是用同步的思维来解决异步问题的。在继续讲解它的语法和用法之前,先介绍一下我们的开发环境是如何支持这种语法的。如果您已经知道如何配置它,则可以跳过它。一、如何在自己的开发环境中支持async/await语法这里主要介绍两种方法。1.webpack中支持该语法首先在当前项目中使用npm下载babel-loader。>npminstallbabel-loader--save-dev然后在配置文件webpack.confing.dev.js中进行配置,在module.exports.module.rules中添加如下配置元素。{test:/\.(js|jsx)$/,include:paths.appSrc,loader:require.resolve('babel-loader'),options:{cacheDirectory:true,},},如果你使用***create-react-app或vue-cli版本来构建您的代码,它们应该已经支持此配置。2.gulp中支持这种语法首先安装gulp插件>npminstallgulp-babel--save-dev然后编写任务vargulp=require('gulp');varbabel=require('gulp-babel');gulp.task('babel',function(){returngulp.src('src/app.js').pipe(babel()).pipe(gulp.dest('dist'));});二、如何使用async函数是Generator的一个语法糖。不知道Generator是什么函数也没关系,我们只需要知道async函数实际上返回的是一个Promise对象即可。asyncfunctionfn(){return30;}//或constfn=async()=>{return30;}在声明函数的时候,在前面加上关键字async,就是async的用法。当我们使用console.log打印出上面声明的函数fn时,可以看到如下结果:console.log(fn());//resultPromise={__proto__:Promise,[[PromiseStatus]]:"resolved",[[PromiseValue]]:30}显然,fn操作的结果其实是一个Promise对象。所以我们也可以使用then来处理后续的逻辑。fn().then(res=>{console.log(res);//30})await表示等待。这意味着代码需要等待await后面的函数运行并返回结果,然后才能继续执行后面的代码。这正是同步所做的。但是需要注意的是,await关键字只能用在async函数中。而await后面的函数运行后必须返回一个Promise对象,才能达到同步的效果。当我们使用变量来接收await的返回值时,返回值就是Promise中resolved的值。//定义一个返回Promise对象的函数functionfn(){returnnewPromise((resolve,reject)=>{setTimeout(()=>{resolve(30);},1000);})}//然后使用async/await完成代码constfoo=async()=>{constt=awaitfn();console.log(t);console.log('nextcode');}foo();//result://30//nextcoderunthis在例子中我们可以看到,当操作在async函数中遇到await时,会等待await后面的函数运行完毕,而不是直接执行下一段代码。如果我们直接使用then方法,想要达到同样的结果,就得把后面的逻辑写在then方法中。constfoo=()=>{returnfn().then(t=>{console.log(t);console.log('nextcode');})}foo();显然如果使用async/await,代码结构会更简洁,逻辑更清晰。异常处理在Promise中,我们知道异常是通过catch捕获的。当我们使用async时,我们使用try/catch来捕获异常。functionfn(){returnnewPromise((resolve,reject)=>{setTimeout(()=>{reject('someerror.');},1000);})}constfoo=async()=>{try{awaitfn();}catch(e){console.log(e);//一些错误}}foo();如果有多个await函数,则只返回第一个捕获的异常。functionfn1(){returnnewPromise((resolve,reject)=>{setTimeout(()=>{reject('someerrorfn1.');},1000);})}functionfn2(){returnnewPromise((resolve,reject)=>{setTimeout(()=>{reject('someerrorfn2.');},1000);})}constfoo=async()=>{try{awaitfn1();awaitfn2();}catch(e){控制台。log(e);//someerrorfn1.}}foo();实践实践中,我们遇到的异步场景大多是接口请求,所以这里通过一个jquery中$.get的简单例子来展示如何配合async/await来解决这种场景。//先定义接口请求方法,由于jquery封装的几个请求方法都返回Promise实例,所以可以直接使用await函数实现同步constgetUserInfo=()=>$.get('xxxx/api/xx');constclickHandler=async()=>{try{constresp=awaitgetUserInfo();//resp为接口返回内容,然后用它来处理相应的逻辑console.log(resp);//dosomething}catch(e){//handleerrorlogic}}为了保证逻辑的完整性,在实践中try/catch是必不可少的。总之,不处理错误逻辑的程序员不是好程序员。与Promise相比,我个人认为async/await有一定的简单性,但相对于Promise并没有绝对的优势,所以只能算是提供了另一种稍微好一点的方式。至于学习后选择哪种方式解决自己的问题,那只是个人喜好的问题。
