Async 和 Await
async 是 ES7 提出的新特性,是 Generator 的语法糖。既然是语法糖,那我们首先说一下它的改进之处。
Async 对 Generator 的改进之处
写法改进
// Generator
function* foo() {
yield 'b'
}
// Async Await
async function foo() {
await 'b'
}
对比发现,async 函数在写法上将 Generator 的星号替换成 async 关键字,yield 关键字替换为 await,更符合异步编程语义化。
内置执行器
Generator 函数必须依靠执行器,而 async 函数自带执行器。所以 async 函数不用像 Generator 函数一样要用 next 方法才会执行,完全可以和普通函数一样。
更好的实用性
co 模块约定,yield 命令后面只能是 Thunk 函数或者是 Promise 对象,而 async 函数的 await 后面可以是任意类型的值,并且会将后面的值转换成为一个立即执行 resolved 的 Promise 对象。
返回值为 Promise
async 函数的返回值是一个 Promise 对象,可以用 then 方法;Generator 函数返回的是一个 Iterator 对象。
Async 基本用法
async 函数返回一个 Promise 对象,可以使用 then 方法添加回调函数。当函数执行的时候,一旦遇到 await 就会先返回,等到一步操作完成,再接着执行函数体内后面的语句,并且 await 必须在 async 函数内,不然会报错。
async 函数中的返回值会成为返回的 Promise 对象中 resolve 的值;当 async 函数中抛出一个错误的时候,会成为返回 Promise 对象中 reject 的值。
Await 表达式
await 语句后面跟随的是一个 thenable 对象(即包含 then 的对象,类 Promise 对象),此时 await 会把他们当成是一个标准的 Promise 来处理,并且返回该 Promise resolved 的值。如果是基本数据类型的话,则会直接返回这个值。
当 await 后面的 Promise 抛出一个错误时,此时会直接跳出 async 函数并被 async 函数的 catch 捕获。如果想要不中断 async 函数继续执行的话,可以尝试在内部对 await 用 try catch 包裹起来。
await 后加 普通函数, 普通函数立即执行, 跟没加await一样
async function test(params) {
await setTimeout(() => {
console.log(1);
}, 1000);
console.log(2);
}
test();
// 2
// 1
function normalFunc() {
console.log('normalFunc');
}
async function test1() {
await normalFunc();
console.log(2);
}
test1();
// normalFunc
// 2
await 串行
getBar() 会等待 getFoo()返回后执行。
async function parell(){
let foo = await getFoo();
let bar = await getBar();
}
await 并行
getBar(),getFoo() 同时执行。
// 写法一
let [foo, bar] = await Promise.all([getFoo(), getBar()]);
// 写法二
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;
async在循环中使用
在forEach中的并行执行的
function dbFuc(db) { //这里不需要 async
let docs = [{}, {}, {}];
// 可能得到错误结果
docs.forEach(async function (doc) {
await db.post(doc);
});
}
在 for...of 和普通for中是串行执行的
async function dbFuc(db) {
let docs = [{}, {}, {}];
for (let doc of docs) {
await db.post(doc);
}
}