简介
async
函数也就一种异步解决方案,是在ES2017引入的,为了使得异步更加方便,它可以说是Generator
函数的语法糖。
下面有一个例子,分别用async
和Generator
写,大家可以看下区别,
//generator
const gen = function* () {
const result = yield readFile("哈哈", 1000)
const results = yield readFile("嘻嘻", 500)
console.log(result)//哈哈
console.log(results)//嘻嘻
}
const readFile = function (data, time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
ge.next(data)
}, time)
})
}
const ge = gen();
ge.next()
//async
const gen=async function(){
const result=await readFile("哈哈",1000)
const results=await readFile("嘻嘻",500)
console.log(result)//哈哈
console.log(results)//嘻嘻
}
const readFile = function (data, time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(data)
}, time)
})
}
gen()
区别:
- 内置执行器,不需要向
Generator
函数,需要用next
方法执行。 - 更好的语义,
async
和await
比起带星号的函数和yield
语义更清楚了。 async
返回值是Promise
基本用法
async
函数返回一个Promise
对象,可以使用then
方法添加回调函数,当函数执行的时候,一旦遇到await
就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。
async function set(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve("111")
})
})
}
async function fn(){
var result=await set();
return result;
}
fn().then((data)=>{
console.log(data)//111
})
语法
返回Promise对象
这个上面例子已经很清楚了,async
函数返回一个Promise
对象,当async
函数有return
语句,那么return
的返回值会被then
接受到,需要注意的是,需要async
函数内部抛出错误,将会被then
的第二个参数或者catch
捕获到。
async function fn(){
throw new Error("出错了")
}
fn().then((data)=>{
console.log(data)
}).catch((err)=>{
console.log(err.message)//出错了
})
另外函数注意的是:
只有async
函数内部的异步操作执行完,才会执行then
方法指定的回调函数。
await命令
正常情况下,await
命令后面是一个 Promise
对象,返回该对象的结果。如果不是 Promise
对象,就直接返回对应的值。
async function f() {
// 等同于
// return 123;
return await 123;
}
f().then(v => console.log(v))
// 123
需要注意:
- 当
await
后面跟的是thenable
对象,那么await
会将其等同于Primise
对象。var obj={ name:'111', then:function(resolve,reject){ setTimeout(()=>{ resolve("hty") }) } } async function fn(){ return await obj } fn().then((data)=>{ console.log(data)//hty })
- 任何一个
await
语句后面的Promise
对象变为reject
状态,那么整个async
函数都会中断执行
但是有时候,我们希望即使第一个报错,后面也跟着执行,这时候我可以用async function f() { await Promise.reject('出错了'); await Promise.resolve('hello world'); // 不会执行 }
try..catch
包裹或者await
后面跟个catch
async function f() { try { await Promise.reject('出错了'); } catch (e) { } return await Promise.resolve('hello world'); } f().then(v => console.log(v))//hello world //或者 async function f1() { await Promise.reject('出错了').catch((err) => { console.log(err) }) return await Promise.resolve('hello world'); } f1().then(v => console.log(v)) //出错了 //hello world
使用注意点
await
命令后面的Promise对象,运行结果可能是rejected
,所以最好把await
命令放在try...catch
代码块中- 多个
await
命令后面的异步操作,如果不存在继发关系,最好让它们同时触发。// 写法一 let [foo, bar] = await Promise.all([getFoo(), getBar()]); // 写法二 let fooPromise = getFoo(); let barPromise = getBar(); let foo = await fooPromise; let bar = await barPromise;
await
命令只能用在async
函数之中,如果用在普通函数,就会报错async
函数可以保留运行堆栈
async 函数实现原理
async
函数的实现原理,就是将 Generator
函数和自动执行器,包装在一个函数里。
async function fn(args) {
// ...
}
// 等同于
function fn(args) {
return spawn(function* () {
// ...
});
}
spawn就是自执行器,结构如下
function spawn(genF) {
return new Promise(function(resolve, reject) {
const gen = genF();
function step(nextF) {
let next;
try {
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); });
});
}