async函数async
函数是Generator函数的语法糖1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17var fs = require('fs');
var readFile = function(fileName){
return new Promise((resolve,reject)=>{
fs.readFile(fileName,function(err,data){
if(err) reject(err);
resolve(data);
})
})
};
var gen = function* (){
var f1 = yield readFile('/etc/fir');
var f2 = yield readFile('/etc/sec');
console.log(f1.toString());
console.log(f2.toString());
}
写成async
:1
2
3
4
5
6var asyncReadFile = async function(){
var f1 = await readFile('/etc/fir');
var f2 = await readFile('/etc/sec');
console.log(f1.toString());
console.log(f2.toString());
}
async
函数对于Generator函数的改进有以下四点
1) 内置执行器async
函数的执行,与普通函数一样,只要一行1
var result = asyncReadFile();
调用上面代码后,自会自动执行,输出最后的结果,不需要使用next
方法,或例如co
之类的模块
2) 更好的语义async/await
和*/yield
相比,语义更好,async
表示函数内部有异步操作,await
表示紧跟在后面的表达式需要等待结果
3) 更广的适用性co
模块规定,yield
命令后只能是thunk
函数或Promise
对象,而async
函数的await
命令后面,可以是Promise、原始类型的值(数值、字符串、布尔值等,但这时等同于同步操作)
4) 返回值是Promise
因为Generator返回的是Iterator对象,所以async
可以使用then
指定下一步操作
用法
当函数执行的时候,遇到await
就停止,等到异步操作完成,再执行后面的语句1
2
3
4
5
6
7
8
9async function getStockPriceByName(name){
var symbol = await getStockSymbol(name);
var stockPrice = await getStockPrice(symbol);
return stockPrice;
}
getStockPriceByName('zhu').then(result=>{
console.log(result)
})
调用上面函数的时候会立即返回Promise
对象
1 | function timeout(ms){ |
asnyc
函数的形式1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29//1) 函数声明
async function (){};
//2) 函数表达式
var foo = async function(){};
//3) 对象
let obj = {
async foo(){}
};
obj.foo().then(...)
//4) class类
class Storage{
constructor(){
this.cachePromise = caches.open('avatars')
}
async getAvatars(name){
const cache = await this.cachePromise;
return cache.match(`/avatars/${name}.jpg`);
}
}
const storage = new Storage();
storage.getAvatar('jake').then(...);
//5) 箭头函数
const foo = async ()=>{}
语法
返回Promise
async
函数内部的return
语句返回的值,会成为then
方法回调函数的参数1
2
3
4
5
6async function f(){
return 'zhu'
}
f().then(v=>console.log(v));
//'zhu'
如果async
函数内部抛出错误,会导致返回的Promise变成reject
,抛出的错误对象会被catch
方法回调函数接收到1
2
3
4
5
6
7async function f(){
throw new Error('error了')
}
f().then(
v=>console.log(v),
e=>console.log(e) //[Error: error了]
)
Promise对象的状态变化
async
函数返回的promise对象,必须等到内部所有的await
命令后面的promise对象执行完,才会有状态改变,除非有return
语句或抛出错误,即只有async
函数内部的异步操作全部执行完,then
方法才会执行
await命令
一般情况下,await
后面是一个promise对象,如果不是,就先转换为一个立即resolve
的Promise对象1
2
3
4
5async function f(){
return await 222;
}
f().then(v=>console.log(v)); //222
await
后面的Promise对象如果为reject
,那么reject
的参数就会被catch
方法的回调函数捕获1
2
3
4
5
6
7async function f(){
await Promise.reject('err')
}
f()
.then(v=>console.log(v))
.catch(e => console.log(e))
即使async
里面没有return
,但仍能被捕获
只要有一个await
后面的Promise变为reject
,那么整个async
函数都会中断执行
如何在前一个异步操作失败后,仍能继续执行后面的异步呢?
1) 可以将第一个await
放在try...catch
结构里面,这样不管第一个成功与否,第二个都能执行,如果有多个也可以放在try...catch
之中1
2
3
4
5
6
7
8
9
10
11
12async function f(){
try{
await Promise.reject('出错了')
}catch(e){
}
return await Promise.resolve('zhu')
}
f()
.then(v=>console.log(v))
2) 在await
后面的promise对象再跟一个catch
方法,处理前面出现的错误1
2
3
4
5
6
7
8async function f(){
await Promise.reject('错误')
.catch(e=>console.log(e));
return await Promise.resolve('zhu')
}
f()
.then(v=>console.log(v))
注意事项
1) await
后面的Promise对象结果可能是rejected
,所以最好把await
放在try...catch
之中1
2
3
4
5
6
7
8
9
10
11
12
13async function myFn(){
try{
await somethingTahtReturnAsPromise();
}catch(err){
console.log(err)
}
}
//另一种写法
async function myFn(){
await somethingTahtReturnAsPromise()
.catch(e=>console.log(e))
}
2) 多个await
后面的异步操作,如果不存在继发关系,最好让他们同时触发1
2let foo = await getFoo();
let bar = await getBar();
上面两个异步操作互相不依赖,写成上面会比较耗时,只有在getFoo
之后才会执行getBar
,可以将他们写成同时触发1
2
3
4
5
6
7
8//写法一
let [foo,bar] = await Promise.all([getFoo(),getBar()]);
//2
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;
3) await
命令只能用在async
函数之中,如果在普通函数会报错1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18async function dbFunc(){
let docs = [{},{},{}];
//报错
docs.forEach(doc=>{
await db.post(doc);
});
//同样报错
docs.forEach(async doc=>{
await db.post(doc);
});
//正确使用for循环
for (let doc of docs) {
await db.post(doc);
}
}
async函数的实现原理
async
函数就是将Generator函数和自动执行器包装在一个函数里面1
2
3
4
5
6
7
8
9
10async function fn(args){
//...
}
//等同于
function fn(){
return spawn(function* (){ //spawn是自动执行器
//...
})
}
spawn
的实现1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23function spawn(genF){
return new Promise(function(resolve,reject){
var gen = genF();
function step(nextF){
try{
var next = nextF();
}catch(e){
return reject(e)
}
if(next.done){
return resolve(next.value)
}
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)})
})
}
与其他异步方法的比较
需求:某个 DOM 元素上面,部署了一系列的动画,前一个动画结束,才能开始后一个。如果当中有一个动画出错,就不再往下执行,返回上一个成功执行的动画的返回值。
1) Promise:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15function chainAniPromise(ele,animation){
var ret = null; //保存上一步动画返回值
var p = Promise.resolve();
for(let anim of animation){ //使用then,添加所有动画,很好
p = p.then(function(value){
ret = value;
return ainm(ele)
})
}
return p.catch(e=>{
//忽略错误,继续执行
}).then(() => ret)
}
2) generator1
2
3
4
5
6
7
8
9
10
11
12
13
14
15function chainAniPromise(ele,animation){
return spawn(function*(){
var ret = null;
try{
for(let anim of animation){
ret = yield anim(ele)
}
}catch(e){
//忽略错误,继续执行
}
return ret;
})
}
3) async1
2
3
4
5
6
7
8
9
10
11async function chainAniPromise(ele,animation){
var ret = null;
try{
for(var anim of animation){
ret = await ainm(ele)
}
}catch(e){
//忽略错误,继续执行
}
return ret
}
异步遍历器
Iterator接口呢只要调用对象的遍历器对象的接口,就会等到一个{value,done}对象,然而这里的next
方法必须是同步的,即一旦执行next
,就必须同步得到value/done
两个属性,这对于异步来说就不适合,所以引入了thunk函数/promise对象
,value
是一个thunk/promise
,等待以后返回的真正的值,而done
还是同步产生
异步遍历器
即为操作提供原生的遍历器接口,value/done
两个属性都是异步产生
异步遍历的接口
最大语法特点就是调用遍历器的next
方法,返回一个promise
1
2
3
4
5
6
7asyncIterator
.next()
.then(
({value,done}=>{
//....
})
)
异步遍历器部署在对象的Symbol.asyncIterator
属性上1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28const asyncIterable = createAsyncIterator(['a','b']);
const asyncIterator = asyncIterable[Symbol.asyncIterator]();
asyncIterator
.next()
.then(iterResult1 =>{
console.log(iterResult1); // { value: 'a', done: false }
return asyncIterator.next();
})
.then(iterResult2 =>{
console.log(iterResult2); // { value: 'b', done: false }
return asyncIterator.next();
})
.then(iterResult3 =>{
console.log(iterResult3); //{ value: undefined, done: true }
})
//改为async
async function f() {
const asyncIterable = createAsyncIterable(['a', 'b']);
const asyncIterator = asyncIterable[Symbol.asyncIterator]();
console.log(await asyncIterator.next());
// { value: 'a', done: false }
console.log(await asyncIterator.next());
// { value: 'b', done: false }
console.log(await asyncIterator.next());
// { value: undefined, done: true }
}
由于异步便利的next
可以连续调用,不必等到上一步产生的Promise对象resolve
以后再调用1
2
3
4
5
6const asyncGenObj = createAsyncIterable(['a','b']);
const [{value:v1},{value:v2}] = await Promise.all([
asyncGenObj.next(),asyncGenObj.next()
]);
console.log(v1,v2); //a,b
for await…of
遍历异步的Iterator接口1
2
3
4
5async function f(){
for await(const x of createAsyncIterable(['a','b'])){
console.log(x)
}
}
createAsyncIterable返回一个异步遍历器,for…of自动调用next
方法,返回Promise对象,用await
来处理Promise对象,一旦resolve
,就把值(x)传入for...of
也可以用于同步遍历器1
2
3
4
5(async function f{
for await (const x of ['a','b']){
console.log(x)
}
})();
异步Generator函数
即async
函数与Generator函数结合1
2
3
4
5
6
7
8
9
10
11
12
13
14
15async function* readLines(path){
let file = await fileOpen(path);
try{
while(!file){
yield await file.readLine()
}
}finally{
await file.close()
}
}
for await (const line of readLines(filePath)){
console.log(line)
}
与for await...of
结合1
2
3
4
5async function* preFixLines(asyncIterable){
for await (const line of asyncIterable){
yield '> '+line;
}
}
yield
依然立刻返回,但返回的是一个Promise