目录
JavaScript 异步编程的终极方案 async / await。
一、async 是做什么的,什么时候工作?
1. async 让函数返回一个 promise 对象。会把函数的返回值通过 promise.resolve() 封装成一个 promise 对象,如果函数没有返回值,通过 promise.resolve(undefined) 封装返回一个 promise 对象。
async function fn() {
return 'foobar';
}
console.log(fn()); // Promise {<resolved>: "foobar"}
函数没有返回值时:
async function fn() {
console.log('foobar');
}
console.log(fn()); // Promise {<resolved>: undefined}
2. async 声明的也是一个函数,因此函数的执行也是在手动调用该函数的时候。
二、await 是做什么的?
1. await 后面接表达式,可以是 promise 对象,也可以不是。
2. await 返回值
如果后面接的是 promise 对象,则返回该对象的结果(resolve 或是 reject 的参数);如果等待的不是promise 对象,就返回其本身。
3.await 必须用在 async 函数内部。
三、async/await 对函数执行顺序的影响。
总的执行顺序:当 async 函数执行到 await 时,会将 await 后面接的函数先执行一遍,然后跳出整个 async 函数,继续执行当前事件循环,当前事件循环结束之后,回到 async 函数中,如果这时 await 函数已经有了结果,那么就继续执行 async 函数,否则继续等待。如果 await 函数返回一个 promise 对象,此时把该对象添加到 microtasks 队列。
看下面的例子:
function commonFunc() {
console.log('commonFunc start...');
return 'commonFunc end';
}
async function fn1() {
console.log('fn1 start...');
return 'fn1 end';
}
async function fn2() {
console.log('fn2 start...');
let res1 = await commonFunc();
console.log(res1);
let res2 = await fn1();
console.log(res2);
}
fn2();
let promise = new Promise(resolve => {
console.log('promise start...');
resolve('this is a promise');
});
promise.then(value => console.log(value));
最终的输出顺序为
fn2 start...
commonFunc start...
promise start...
commonFunc end
fn1 start...
this is a promise
fn1 end
结合代码我们来具体分析
/* 注意 commonFunc 是一个普通函数 */
function commonFunc() {
console.log('commonFunc start...');
return 'commonFunc end';
}
/* fn1 是一个 async 函数,将被处理为一个 promise 对象*/
async function fn1() {
console.log('fn1 start...');
return 'fn1 end';
}
async function fn2() {
console.log('fn2 start...'); // ① 第一个输出
let res1 = await commonFunc(); // ② fn2 顺序执行到这里,先把 await 后面的函数执行一遍
// 因此第二个输出了 commonFunc 里面的 'commonFunc start...'
// 然后会第一次跳出整个 fn2,继续执行后面的task。
console.log(res1); // ④ task 执行完毕之后,回到 fn2
// commonFunc 只是一个普通的函数,将返回值赋值给 res1 后输出。
let res2 = await fn1(); // ⑤ 同样,先执行一遍 fn1 ,输出 'fn1 start...'
// 由于 fn1 是一个 promise 对象,因此此时把 这个 promise 推到 micro tasks 队列中。
// 第二次跳出fn2.
console.log(res2); // ⑦ 执行完 micro tasks 队列首位的 promise 后,再次回到 fn2
// 此时 fn1 已经等待结束,输出 'fn1 end'
}
fn2(); // 这里执行了 fn2, 所以 'fn2 start...'第一个被输出
let promise = new Promise(resolve => {
console.log('promise start...'); // ③ 第一次跳出 fn2 后,先执行到这里,所以第三个输出
// 将这个 promise 推到 micro tasks 队列的首位
resolve('this is a promise');
});
promise.then(value => console.log(value)); // ⑥ 第二次跳出 fn2 之后,task 已经执行完毕,开始执行 micro tasks 队列。
// 由于这个 promise 位于 micro tasks 的首位,因此第6个输出。
四、 写在最后
如果能够把上面的例子完全弄明白,可能需要了解 task 与 micro tasks,参照https://www.cnblogs.com/QH-Jimmy/p/6493389.html。之所以不使用 setTimeout来模拟耗时事件,是因为 setTimeout 的回调函数会创建另一个 task,肯定会最后执行,就不是在一个事件轮询中了。
本文详细解释了JavaScript中async和await的功能与作用时机,包括async如何使函数返回Promise对象,await如何暂停和恢复异步执行流程,以及它们如何影响函数执行顺序。
3904

被折叠的 条评论
为什么被折叠?



