Skip to content

Commit b88e6b6

Browse files
committed
修改promise/async
1 parent caacc57 commit b88e6b6

File tree

2 files changed

+126
-3
lines changed

2 files changed

+126
-3
lines changed

docs/promise.md

Lines changed: 125 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -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函数将这个过程在语言内部处理掉了,不暴露给用户。

docs/reference.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
- Jafar Husain, [Async Generators](https://docs.google.com/file/d/0B4PVbLpUIdzoMDR5dWstRllXblU/view?sle=true): 对async与Generator混合使用的一些讨论
8282
- Axel Rauschmayer, [ECMAScript 6 promises (2/2): the API](http://www.2ality.com/2014/10/es6-promises-api.html): 对ES6 Promise规格和用法的详细介绍
8383
- Jack Franklin, [Embracing Promises in JavaScript](http://javascriptplayground.com/blog/2015/02/promises/): catch方法的例子
84+
- Luke Hoban, [Async Functions for ECMAScript](https://github.com/lukehoban/ecmascript-asyncawait): Async函数的设计思想,与Promise、Gernerator函数的关系
8485

8586
## Class与模块
8687

0 commit comments

Comments
 (0)