async和await关键字让我们可以用一种更简洁的方式写出基于Promise的异步行为,而无需刻意地链式调用promise。
在其出现之前,开发者只能通过 链式.then() 的方式处理promise异步操作,这样写出的代码冗余且不易读
async function name([param[, param[, ... param]]]) {
statements
}
name
函数名称
param
要传递给函数的参数的名称
statements
包含函数主题的表达式,可以await
机制,且await
必须包含在async
之下
一个Promise
,这个promise要么会通过一个由async函数返回的值被解决,要么会通过一个从async函数中抛出的(或其中没有被捕获到的)异常被拒绝。
async函数可能包含0个或者多个await表达式。await表达式会暂停整个async函数的执行进程并出让其控制权,只有当其等待的基于promise的异步操作被兑现或被拒绝之后才会恢复进程。promise的解决值会被当作该await表达式的返回值。
在async中第一个await之前的代码会被同步执行,遇到await之后开始异步操作,就会退出当前async函数的执行进而执行函数下面的操作,直到那个await异步操作完成,会继续async函数里下面的同步操作,若再遇到await又会开启一个异步操作,并且退出当前async函数继续其下面的步骤,一直等到这个await操作完成。
async函数一定会返回一个promise对象。如果一个async函数的返回值看起来不是promise,那么它将会被隐式地包装在一个promise中。
async function foo() {
return 1
}
//等价于
function foo() {
return Promise.resolve(1)
}
使用async / await关键字就可以在异步代码中使用普通的try / catch代码块。
注意:promise链不是一次就构建好的,相反,promise链是分阶段构造的,因此在处理异步函数时必须注意对错误函数的处理。
例如,在下面的代码中,在promise链上配置了.catch处理程序,将抛出未处理的promise错误。这是因为p2返回的结果不会被await处理。
async function foo() {
const p1 = new Promise((resolve) => setTimeout(() => resolve('1'), 1000))
const p2 = new Promise((_,reject) => setTimeout(() => reject('2'), 500))
const results = [await p1, await p2] // 不推荐使用这种方式,请使用 Promise.all或者Promise.allSettled
//await Promise.all([p1, p2])
}
foo().catch(() => {}) // 捕捉所有的错误...
async函数的函数体可以被看作是由0个或者多个await表达式分割开来的。从第一行代码直到(并包括)第一个await表达式(如果有的话)都是同步运行的。
这样的话,一个不含await表达式的async函数是会同步运行的。然而,如果函数体内有一个await表达式,async函数就一定会异步执行。
async function foo() {
await 1
}
//等价于
function foo() {
return Promise.resolve(1).then(() => undefined)
}
await 表达式会暂停当前 async function 的执行(),等待 Promise 处理完成。若 Promise 正常处理(fulfilled),其回调的resolve函数参数作为 await 表达式的值,继续执行 async function。
在async中第一个await之前的代码会被同步执行,遇到await之后开始异步操作,就会退出当前async函数的执行进而执行函数下面的操作,直到那个await异步操作完成,会继续async函数里下面的同步操作,若再遇到await又会开启一个异步操作,并且退出当前async函数继续其下面的步骤,一直等到这个await操作完成。
若 Promise 处理异常(rejected),await 表达式会把 Promise 的异常原因抛出。
另外,如果 await 操作符后的表达式的值不是一个 Promise,则返回该值本身。
function resolveAfter2Seconds(x) {
return new Promise(resolve => {
setTimeout(() => {
resolve(x);
}, 2000);
});
}
async function f1() {
var x = await resolveAfter2Seconds(10);
console.log(x); // 10
}
f1();
JavaScript主线程不断重复上面的过程
import thenFs from 'then-fs'
console.log('A')
async function getAllFile() {
console.log('B')
const r1 = await thenFs.readFile('./files/1.txt', 'utf8')
const r2 = await thenFs.readFile('./files/2.txt', 'utf8')
const r3 = await thenFs.readFile('./files/3.txt', 'utf8')
console.log(r1,r2,r3)
console.log('D')
}
getAllFile()
console.log('C')
运行结果
A
B
C
111 222 333
D
var slowPromise = function() {
console.log("starting slow promise");
return new Promise(resolve => {
setTimeout(function() {
resolve("slow");
console.log("slow promise is done");
}, 2000);
});
};
var fastPromise = function() {
console.log("starting fast promise");
return new Promise(resolve => {
setTimeout(function() {
resolve("fast");
console.log("fast promise is done");
}, 1000);
});
};
var sequentialStart = async function() {
console.log('=========SEQUENTIAL START===========');
// 1. Execution gets here almost instantly
const slow = await slowPromise();
console.log(slow); // 2. this runs 2 seconds after 1.
const fast = await fastPromise();
console.log(fast); // 3. this runs 3 seconds after 1.
}
var concurrentStart = async function() {
console.log('===========CONCURRENT START with await===========');
const slow = slowPromise(); // starts timer immediately
const fast = fastPromise(); // starts timer immediately
// 1. Execution gets here almost instantly
console.log(await slow); // 2. this runs 2 seconds after 1.
console.log(await fast); // 3. this runs 2 seconds after 1., immediately after 2., since fast is already resolved
}
var concurrentPromise = function() {
console.log('===========CONCURRENT START with Promise.all===========');
return Promise.all([slowPromise(), fastPromise()]).then((messages) => {
console.log(messages[0]); // slow
console.log(messages[1]); // fast
});
}
var parallel = async function() {
console.log('===========PARALLEL with await Promise.all===========');
// Start 2 "jobs" in parallel and wait for both of them to complete
await Promise.all([
(async()=>console.log(await slowPromise()))(),
(async()=>console.log(await fastPromise()))()
]);
}
// This function does not handle errors. See warning below!
var parallelPromise = function() {
console.log('===========PARALLEL with Promise.then===========');
slowPromise().then((message)=>console.log(message));
fastPromise().then((message)=>console.log(message));
}
sequentialStart(); // after 2 seconds, logs "slow", then after 1 more second, "fast"
// wait above to finish
setTimeout(concurrentStart, 4000); // after 2 seconds, logs "slow" and then "fast"
// wait again
setTimeout(concurrentPromise, 7000); // same as concurrentStart
// wait again
setTimeout(parallel, 10000); // truly parallel: after 1 second, logs "fast", then after 1 more second, "slow"
// wait again
setTimeout(parallelPromise, 13000); // same as parallel
运行结果
=========SEQUENTIAL START===========
starting slow promise
slow promise is done
slow
starting fast promise
fast promise is done
fast
===========CONCURRENT START with await===========
starting slow promise
starting fast promise
fast promise is done
slow promise is done
slow
fast
===========CONCURRENT START with Promise.all===========
starting slow promise
starting fast promise
fast promise is done
slow promise is done
slow
fast
===========PARALLEL with await Promise.all===========
starting slow promise
starting fast promise
fast promise is done
fast
slow promise is done
slow
===========PARALLEL with Promise.then===========
starting slow promise
starting fast promise
fast promise is done
fast
slow promise is done
slow
个人感觉这块很不容易理解,建议多测试一下样例,体会一下其中精妙~