1
0
mirror of https://github.com/ruanyf/es6tutorial.git synced 2025-05-24 10:22:23 +00:00

修改promise/async

This commit is contained in:
Ruan Yifeng 2015-02-16 22:27:58 +08:00
parent caacc57d9f
commit b88e6b611e
2 changed files with 126 additions and 3 deletions

View File

@ -383,9 +383,13 @@ run(g);
## async函数
async函数是用来取代回调函数的另一种方法。
### 概述
只要函数名之前加上async关键字就表明该函数内部有异步操作。该异步操作应该返回一个Promise对象前面用await关键字注明。当函数执行的时候一旦遇到await就会先返回等到触发的异步操作完成再接着执行函数体内后面的语句。
async函数与Promise、Generator函数一样是用来取代回调函数、解决异步操作的另一种方法。它可以写出比Promise和Generator更简洁易读的代码但是依赖这两者来实现。
async函数并不属于ES6而是被列入了ES7但是traceur编译器和regenerator转码器已经实现了这个功能。
在用法上只要函数名之前加上async关键字就表明该函数内部有异步操作。该异步操作应该返回一个Promise对象前面用await关键字注明。当函数执行的时候一旦遇到await就会先返回等到触发的异步操作完成再接着执行函数体内后面的语句。
```javascript
@ -417,4 +421,122 @@ async function asyncValue(value) {
上面代码中asyncValue函数前面有async关键字表明函数体内有异步操作。执行的时候遇到await语句就会先返回等到timeout函数执行完毕再返回value。
async函数并不属于ES6而是被列入了ES7但是traceur编译器已经实现了这个功能。
### 与Promise、Generator的比较
我们通过一个例子来看Async函数与Promise、Generator函数的区别。
假定某个DOM元素上面部署了一系列的动画前一个动画结束才能开始后一个。如果当中有一个动画出错就不再往下执行返回上一个成功执行的动画的返回值。
首先是Promise的写法。
```javascript
function chainAnimationsPromise(elem, animations) {
// 变量ret用来保存上一个动画的返回值
var ret = null;
// 新建一个空的Promise
var p = Promise.resolve();
// 使用then方法添加所有动画
for(var anim in animations) {
p = p.then(function(val) {
ret = val;
return anim(elem);
})
}
// 返回一个部署了错误捕捉机制的Promise
return p.catch(function(e) {
/* 忽略错误,继续执行 */
}).then(function() {
return ret;
});
}
```
虽然Promise的写法比回调函数的写法大大改进但是一眼看上去代码完全都是Promise的APIthen、catch等等操作本身的语义反而不容易看出来。
接着是Generator函数的写法。
```javascript
function chainAnimationsGenerator(elem, animations) {
return spawn(function*() {
var ret = null;
try {
for(var anim of animations) {
ret = yield anim(elem);
}
} catch(e) {
/* 忽略错误,继续执行 */
}
return ret;
});
}
```
上面代码使用Generator函数遍历了每个动画语义比Promise写法更清晰用户定义的操作全部都出现在spawn函数的内部。这个写法的问题在于必须有一个任务运行器自动执行Generator函数上面代码的spawn函数就是任务运行器它返回一个Promise对象而且必须保证yield语句后面的表达式必须返回一个Promise。下面是spawn函数的代码。
```javascript
function spawn(genF) {
// 返回一个Promise
return new Promise(function(resolve, reject) {
// 执行Generator函数返回一个遍历器
var gen = genF();
// 定义一个函数,执行每一个任务
function step(nextF) {
var next;
try {
next = nextF();
} catch(e) {
// 如果任务执行出错Promise状态变为已失败
reject(e);
return;
}
if(next.done) {
// 所有任务执行完毕Promise状态变为已完成
resolve(next.value);
return;
}
// 如果还有下一个任务就继续调用step方法
Promise.resolve(next.value).then(function(v) {
step(function() { return gen.next(v); });
}, function(e) {
step(function() { return gen.throw(e); });
});
}
step(function() { return gen.next(undefined); });
});
}
```
最后是Async函数的写法。
```javascript
async function chainAnimationsAsync(elem, animations) {
var ret = null;
try {
for(var anim of animations) {
ret = await anim(elem);
}
} catch(e) {
/* 忽略错误,继续执行 */
}
return ret;
}
```
可以看到Async函数的实现最简洁最符合语义几乎没有语义不相关的代码。它实际上将Generator写法中的任务运行器改在语言层面提供因此代码量最少。Generator写法的spawn函数本质是将Generator函数转为Promise对象Async函数将这个过程在语言内部处理掉了不暴露给用户。

View File

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