生成器函数是一个带星号的函数,能够暂停执行与恢复执行。 async/await 使用了 协程(Generator) 和 微任务(Promise) 两种技术来实现。promise
function* genDemo() {
console.log("开始执行第 1 段");
yield "generator 1";
console.log("开始执行第 2 段");
yield "generator 2";
console.log("执行结束");
return "generator 3";
}
console.log("main 0");
let gen = genDemo(); // 此处不会打印"开始执行第 1 段"只有在执行 gen.next 时才会执行
console.log(gen.next().value); // 开始执行第 1 段 generator 1
console.log("main 1");
console.log(gen.next().value);
console.log("main 2");
console.log(gen.next().value);
console.log("main 3");
复制代码
从上面输出结果能够看出,生成器函数与主函数是交替执行的。
生成器函数中遇到 yield
关键字时,就会返回 yield 后的内容给外部并把执行权交给外部函数去执行。
外部函数又能够经过 gen.next
恢复生成器函数的执行。浏览器
协程是一种比线程更加轻量级的存在,能够当作是跑在线程上的任务。就像一个进程能够有多个线程同样,一个线程也能够有多个协程。
可是,线程上同时只能执行一个协程。好比:当前执行的是 A 协程,要启动 B,就须要将主线程的控制权交给 B 协程;A 暂停执行,B 恢复执行。一般,若是从 A 协程启动 B 协程,咱们就把 A 协程称为 B 协程的父协程。并发
async 是一个经过异步执行并隐式返回 Promise做为结果的函数。异步
async function foo() {
return 2;
}
foo(); // Promise {<resolved>: 2}
复制代码
观察下面代码的输出:async
async function foo() {
console.log(1);
let a = await 100;
console.log(a);
console.log(2);
}
console.log(0);
foo();
console.log(3);
复制代码
输出:0 1 3 100 2 执行流程图以下:
函数
let promise_ = new Promise((resolve, reject) => {
resolve(100);
});
复制代码
JS 引擎会将该任务提交到微任务队列,而后暂停当前协程的执行,将主线程的控制权转交给父协程执行,同时将 promise_ 对象返回给父协程(以下)。ui
async function foo() {
...
let a = await 100
...
}
console.log(foo())
//Promise {<pending>}__proto__: ... "
// [[PromiseStatus]]: "resolved"
// [[PromiseValue]]: undefined
复制代码
主线程控制权交给父协程后,父协程调用 promise_.then 来监控 promise 状态的改变。spa
接下来执行父协程的流程,打印出 3。随后父协程将执行结束,在结束前,进入微任务的检查点去执行微任务队列,微任务队列中有 resolve(100) 等待执行,执行到这里时,会触发 promise_.then 中的回调函数,以下:线程
promise_.then(value => {
// 回调函数触发后,将主线程的控制权交给 foo 协程,并将 value 传给协程
});
复制代码
foo 协程激活后,将 value 的值给了变量 a,而后继续执行后面语句,执行完成,将控制权归还给父协程。code
async function foo() {
console.log("foo");
}
async function bar() {
console.log("bar start");
await foo();
console.log("bar end");
}
console.log("script start");
setTimeout(() => {
console.log("setTimeout");
}, 0);
bar();
new Promise(resolve => {
console.log("promise executor");
resolve();
}).then(() => {
console.log("promise then");
});
console.log("script end");
复制代码
输出以下:
scritp start
bar start foo
promise executor
script end
bar end
promise then
setTimeout
注意点: 第三步会输出 foo,而不是 promise executor. 由于 await 是将 return 的值用 resolve 包装提交到微任务队列,console.log 语句不受影响,能够直接输出。
setTimeout 被放到延迟队列中,而不是下一轮宏任务。
本轮宏任务执行完成后,会执行延迟队列中的任务。
宏任务中父协程执行结束前,会去微任务队列检查执行微任务。