@@ -383,9 +383,13 @@ run(g);
383383
384384## async函数
385385
386- async函数是用来取代回调函数的另一种方法。
386+ ### 概述
387387
388- 只要函数名之前加上async关键字,就表明该函数内部有异步操作。该异步操作应该返回一个Promise对象,前面用await关键字注明。当函数执行的时候,一旦遇到await就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句。
388+ async函数与Promise、Generator函数一样,是用来取代回调函数、解决异步操作的另一种方法。它可以写出比Promise和Generator更简洁易读的代码,但是依赖这两者来实现。
389+
390+ async函数并不属于ES6,而是被列入了ES7,但是traceur编译器和regenerator转码器已经实现了这个功能。
391+
392+ 在用法上,只要函数名之前加上async关键字,就表明该函数内部有异步操作。该异步操作应该返回一个Promise对象,前面用await关键字注明。当函数执行的时候,一旦遇到await就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句。
389393
390394``` javascript
391395
@@ -417,4 +421,122 @@ async function asyncValue(value) {
417421
418422上面代码中,asyncValue函数前面有async关键字,表明函数体内有异步操作。执行的时候,遇到await语句就会先返回,等到timeout函数执行完毕,再返回value。
419423
420- async函数并不属于ES6,而是被列入了ES7,但是traceur编译器已经实现了这个功能。
424+ ### 与Promise、Generator的比较
425+
426+ 我们通过一个例子,来看Async函数与Promise、Generator函数的区别。
427+
428+ 假定某个DOM元素上面,部署了一系列的动画,前一个动画结束,才能开始后一个。如果当中有一个动画出错,就不再往下执行,返回上一个成功执行的动画的返回值。
429+
430+ 首先是Promise的写法。
431+
432+ ``` javascript
433+
434+ function chainAnimationsPromise (elem , animations ) {
435+
436+ // 变量ret用来保存上一个动画的返回值
437+ var ret = null ;
438+
439+ // 新建一个空的Promise
440+ var p = Promise .resolve ();
441+
442+ // 使用then方法,添加所有动画
443+ for (var anim in animations) {
444+ p = p .then (function (val ) {
445+ ret = val;
446+ return anim (elem);
447+ })
448+ }
449+
450+ // 返回一个部署了错误捕捉机制的Promise
451+ return p .catch (function (e ) {
452+ /* 忽略错误,继续执行 */
453+ }).then (function () {
454+ return ret;
455+ });
456+
457+ }
458+
459+ ```
460+
461+ 虽然Promise的写法比回调函数的写法大大改进,但是一眼看上去,代码完全都是Promise的API(then、catch等等),操作本身的语义反而不容易看出来。
462+
463+ 接着是Generator函数的写法。
464+
465+ ``` javascript
466+
467+ function chainAnimationsGenerator (elem , animations ) {
468+
469+ return spawn (function * () {
470+ var ret = null ;
471+ try {
472+ for (var anim of animations) {
473+ ret = yield anim (elem);
474+ }
475+ } catch (e) {
476+ /* 忽略错误,继续执行 */
477+ }
478+ return ret;
479+ });
480+
481+ }
482+
483+ ```
484+
485+ 上面代码使用Generator函数遍历了每个动画,语义比Promise写法更清晰,用户定义的操作全部都出现在spawn函数的内部。这个写法的问题在于,必须有一个任务运行器,自动执行Generator函数,上面代码的spawn函数就是任务运行器,它返回一个Promise对象,而且必须保证yield语句后面的表达式,必须返回一个Promise。下面是spawn函数的代码。
486+
487+ ``` javascript
488+
489+ function spawn (genF ) {
490+ // 返回一个Promise
491+ return new Promise (function (resolve , reject ) {
492+ // 执行Generator函数,返回一个遍历器
493+ var gen = genF ();
494+
495+ // 定义一个函数,执行每一个任务
496+ function step (nextF ) {
497+ var next;
498+ try {
499+ next = nextF ();
500+ } catch (e) {
501+ // 如果任务执行出错,Promise状态变为已失败
502+ reject (e);
503+ return ;
504+ }
505+ if (next .done ) {
506+ // 所有任务执行完毕,Promise状态变为已完成
507+ resolve (next .value );
508+ return ;
509+ }
510+ // 如果还有下一个任务,就继续调用step方法
511+ Promise .resolve (next .value ).then (function (v ) {
512+ step (function () { return gen .next (v); });
513+ }, function (e ) {
514+ step (function () { return gen .throw (e); });
515+ });
516+ }
517+
518+ step (function () { return gen .next (undefined ); });
519+ });
520+ }
521+
522+ ```
523+
524+ 最后是Async函数的写法。
525+
526+ ``` javascript
527+
528+ async function chainAnimationsAsync (elem , animations ) {
529+ var ret = null ;
530+ try {
531+ for (var anim of animations) {
532+ ret = await anim (elem);
533+ }
534+ } catch (e) {
535+ /* 忽略错误,继续执行 */
536+ }
537+ return ret;
538+ }
539+
540+ ```
541+
542+ 可以看到Async函数的实现最简洁,最符合语义,几乎没有语义不相关的代码。它实际上将Generator写法中的任务运行器,改在语言层面提供,因此代码量最少。Generator写法的spawn函数本质是将Generator函数转为Promise对象,Async函数将这个过程在语言内部处理掉了,不暴露给用户。
0 commit comments