async函数就是Generator函数的语法糖。async函数的实现原理就是Generator函数 + 自动执行器包装在一个函数里。es6
asycn函数 | Generator函数 |
---|---|
自带执行器 | 执行必须靠执行器 |
async/await语义清晰 | 星号和yield语义不明确 |
await命令后Promise对象和原型类型值 | Thunk函数或Promise |
返回值Promise,可使用then命令 | 返回值Iterator对象 |
aysnc函数返回一个Promise对象,aysnc函数内部return语句返回值,做为then方法回调函数的参数。当函数执行时候,一旦遇到await就会先返回,等到异步操做完成,再接着执行函数体内后面的语句。ajax
function sleep(ms) {
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, ms);
});
}
async function foo(ms){
await sleep(ms);
return 300;
}
foo(2000).then((data)=> {
console.log(data)
});
//300
复制代码
调用foo会当即返回一个promise,2s后await后面的promise状态变成resolve,才执行return,执行then,打印300。promise
因为async返回是promise,程序能够改成bash
async function sleep(ms) {
await new Promise(resolve => {
setTimeout(() => {
resolve();
}, ms);
});
}
//...其余代码同上
复制代码
await命令后是一个Promise对象。若是不是,会被转成一个当即resolve的Promise。若是await命令后面是一个thenable对象(即定义then方法的对象),那么await会将其等同于 Promise 对象。能够参照es6之Promise深刻理解并发
let thenable = {
then: (resolve) => {
console.log('thenable');
resolve('xxx');
}
}
async function foo(){
return await thenable;
}
foo().then((data)=> {
console.log(data)
}).catch((e)=>{
console.log(e.toString())
});
//thenable
//xxx
复制代码
内部抛出错误,后面语句不执行,直接外部catch捕获错误异步
async function foo(){
throw new Error('出错了');
await sleep(); //不执行
}
foo().then((data)=> {
console.log(data)
}).catch((e)=>{
console.log(e.toString())
});
//Error: 出错了
复制代码
这种状况会致使返回的Promise对象状态为reject状态,错误被catch方法回调函数接收到。async
async函数返回的Promise对象必须等到内部全部await命名后面的Promise对象执行完才会发生状态改变,除非遇到return语句或者抛出错误。函数
任何一个await语句后面的Promise对象变为reject状态,那么整个async函数都会中断执行。post
内部本身捕获,程序继续往下执行return语句ui
async function foo(){
await Promise.reject('error').catch((e)=>{
console.log('in:', e.toString())
});
return 300;
}
foo().then((data)=> {
console.log(data)
}).catch((e)=>{
console.log('out:',e.toString())
});
//in: error
//300
复制代码
const NUM_RETRIES = 3;
async function multiRetries(url, count){
let c = count || NUM_RETRIES;
for(let i = 0; i < c; i++){
try {
await ajax.get(url);
break; //请求成功就跳出循环
} catch (error) {
console.error(error);
}
}
}
multiRetries('http://xxxx').then((data)=> {
console.log(data)
}).catch((e)=>{
console.log(e.toString())
});
复制代码
多个await命令后面的异步操做,若是不存在继发关系,最好让他们同时触发
let [foo, bar] = await Promise.all([ajax.get(), ajax.post()])
//或
let fooPromise = ajax.get();
let barPromise = ajax.get();
let foo = await fooPromise;
let bar = await fooPromise;
复制代码
forEach中使用await中注意,以下是并发的。由于只有async函数内部是继发执行的,外部不受影响。
urls.forEach(async function(url){
await ajax.get(url);
});
复制代码
Iterator 接口是一种数据遍历的协议,只要调用遍历器对象的 next 方法就会获得一个对象。特别注意 next 方法必须是同步的,只要调用就必须马上返回值。
Generator函数里面的异步操做返回一个Thunk函数或者Promise对象,等待之后返回真正的值,done属性仍是同步产生的。
async function foo(){
try {
for await (let value of createAsyncIterable()){
console.log(value);
}
} catch (e) {
console.log(e.toString());
}
}
复制代码
await后面操做的应该返回Promise对象。使用yield关键字的地方,就是next方法停下来的地方。
async function* gen(){
yield await Promise.resolve('xxx');
}
复制代码
注:读阮一峰老师的《ES6入门标准》作的笔记