前言asyncawait的语法出现在ES7,基于ES6的promise和generator实现。生成器函数这里不再赘述生成器,专文会讲到特别的内容。await在等待什么我们来看下面的代码,这是asyncawait最简单的用法,await之后返回的是一个Promise对象:asyncfunctiongetResult(){awaitnewPromise((resolve,reject)=>{setTimeout(()=>{resolve(1);console.log(1);},1000);})}getResult()但是不知道大家有没有想过一个问题,为什么要等到状态返回的promise对象是non-pending时会继续执行,也就是resolve执行完后,会继续执行,就像下面的代码。asyncfunctiongetResult(){awaitnewPromise((resolve,reject)=>{setTimeout(()=>{resolve(1);console.log(1);},1000);})控制台。log(2);}getResult()可以看到运行的结果是先打印1,再打印2,也就是说返回的promise对象在执行resolve()之前,一直在等待等待它执行。然后执行下面的程序,这是怎么实现的呢?原理实现我们来看看下面的代码,输出顺序是什么?asyncfunctiongetResult(){awaitnewPromise((resolve,reject)=>{setTimeout(()=>{resolve(1);console.log(1);},1000);})awaitnewPromise((resolve,reject)=>{setTimeout(()=>{resolve(2);console.log(2);},500);})awaitnewPromise((resolve,reject)=>{setTimeout(()=>{resolve(3);console.log(3);},100);})}getResult()对,就是1,2,3。使用生成器函数来实现这个效果怎么样?一开始我是这样实现的:function*getResult(params){yieldnewPromise((resolve,reject)=>{setTimeout(()=>{resolve(1);console.log(1);},1000);})yieldnewPromise((resolve,reject)=>{setTimeout(()=>{resolve(2);console.log(2);},500);})yieldnewPromise((resolve,reject)=>{setTimeout(()=>{resolve(3);console.log(3);},100);})}constgen=getResult()gen.next();gen.next();gen.next();但是发现打印顺序是3,2,1。显然是错误的。这里的主要问题是三个新的承诺几乎同时执行。这种问题只会出现,所以需要等第一个promise执行完resolve之后再执行下一个,所以必须要这样实现。function*getResult(params){yieldnewPromise((resolve,reject)=>{setTimeout(()=>{resolve(1);console.log(1);},1000);})yieldnewPromise((resolve,reject)=>{setTimeout(()=>{resolve(2);console.log(2);},500);})yieldnewPromise((resolve,reject)=>{setTimeout(()=>{resolve(3);console.log(3);},100);})}constgen=getResult()gen.next().value.then(()=>{gen.next().value.then(()=>{gen.next();});});可以看到打印正常。优化但是,你不能有太多的等待,你必须自己写尽可能多的嵌套,所以你仍然需要封装一个函数。显然,递归实现是最简单的。function*getResult(params){yieldnewPromise((resolve,reject)=>{setTimeout(()=>{resolve(1);console.log(1);},1000);})yieldnewPromise((resolve,reject)=>{setTimeout(()=>{resolve(2);console.log(2);},500);})yieldnewPromise((resolve,reject)=>{setTimeout(()=>{resolve(3);console.log(3);},100);})}constgen=getResult()functionco(g){g.next().value.then(()=>{co(g)})}co(gen)再看打印结果:可以发现执行成功了,但是为什么会报错呢?这是因为生成器方法返回了四次,最后一个值是未定义的。其实第三次返回就是done已经返回了,也就是结束了。所以,我们要判断是否已经完成,不要让它继续递归。所以可以改成这样:function*getResult(params){yieldnewPromise((resolve,reject)=>{setTimeout(()=>{resolve(1);console.log(1);},1000);})yieldnewPromise((resolve,reject)=>{setTimeout(()=>{resolve(2);console.log(2);},500);})yieldnewPromise((resolve,reject)=>{setTimeout(()=>{resolve(3);console.log(3);},100);})}constgen=getResult()functionco(g){constnextObj=g.next();如果(nextObj.done){返回;}nextObj.value.then(()=>{co(g)})}co(gen)可以看到这样实现了。完美,这个co实际上是著名co函数的简单写法。这篇关于async和await原理的文章就到此为止了,再讲下去就不礼貌了。
