Promise, Generator, async & await
1.1 Promise 作用
Promise 大家应该都用过,ajax 库就是利用 Promise 封装的; 作用主要是解决地狱回调问题。
1.2 使用
1.2.1.方法一
new Promise((resolve, reject) => {
resolve("这是第一个 resolve 值");
})
.then((data) => {
console.log(data); //会打印'这是第一个 resolve 值'
})
.catch(() => {});
new Promise((resolve, reject) => {
reject("这是第一个 reject 值");
})
.then((data) => {
console.log(data);
})
.catch((data) => {
console.log(data); //会打印'这是第一个 reject 值'
});
1.2.2.方法二(静态方法)
Promise.resolve("这是第二个 resolve 值").then((data) => {
console.log(data); // 会打印'这是第二个 resolve 值'
});
Promise.reject("这是第二个 reject 值")
.then((data) => {
console.log(data);
})
.catch((data) => {
console.log(data); //这是第二个 reject 值
});
1.2.3.方法三(多个 Promise 并行执行异步操作)
表示多个 Promise 都进入到 FulFilled 或者 Rejected 就会执行
const pOne = new Promise((resolve, reject) => {
resolve(1);
});
const pTwo = new Promise((resolve, reject) => {
resolve(2);
});
const pThree = new Promise((resolve, reject) => {
resolve(3);
});
Promise.all([pOne, pTwo, pThree]).then(
(data) => {
console.log(data); // [1, 2, 3] 正常执行完毕会执行这个,结果顺序和promise实例数组顺序是一致的
},
(err) => {
console.log(err); // 任意一个报错信息
}
);
1.2.4.方法四(多个中一个正常执行)
表示多个 Promise 只有一个进入到 FulFilled 或者 Rejected 就会执行
const pOne = new Promise((resolve, reject) => {
resolve(1);
});
const pTwo = new Promise((resolve, reject) => {
resolve(2);
});
const pThree = new Promise((resolve, reject) => {
// resolve(3);
});
Promise.race([pOne, pTwo, pThree]).then(
(data) => {
console.log(data); // 1 只要碰到FulFilled 或者 Rejected就会停止执行
},
(err) => {
console.log(err); // 任意一个报错信息
}
);
1.3 作用分析
1.3.1 参数和状态
- Promise 接受一个函数 handle 作为参数,handle 包括 resolve 和 reject 两个是函数的参数
- Promise 相当于一个状态机,有三种状态:pending,fulfilled,reject,初始状态为 pending
- 调用 resolve,状态由 pending => fulfilled
- 调用 reject,会由 pending => rejected
- 改变之后不会变化
1.3.2 then 方法
-
接受两个参数,onFulfilled 和 onRejected 可选的函数
-
不是函数必须被忽略
-
onFullfilled: A. 当 promise 状态变为成功时必须被调用,其第一个参数为 promise 成功状态传入的值( resolve 执行时传入的值 ) B. 在 promise 状态改变前其不可被调用 C. 其调用次数不可超过一次
-
onRejected:作用和 onFullfilled 类似,只不过是 promise 失败调用
-
then 方法可以链式调用 A. 每次返回一个新的 Promise B. 执行规则和错误捕获:then 的返回值如果是非 Promise 直接作为下一个新 Promise 参数,如果是 Promise 会等 Promise 执行
// 返回非 Promise let promise1 = new Promise((resolve, reject) => { setTimeout(() => { resolve(); }, 1000); }); promise2 = promise1.then((res) => { // 返回一个普通值 return "这里返回一个普通值"; }); promise2.then((res) => { console.log(res); //1秒后打印出:这里返回一个普通值 }); // 返回 Promise let promise1 = new Promise((resolve, reject) => { setTimeout(() => { resolve(); }, 1000); }); promise2 = promise1.then((res) => { // 返回一个 Promise 对象 return new Promise((resolve, reject) => { setTimeout(() => { resolve("这里返回一个Promise"); }, 2000); }); }); promise2.then((res) => { console.log(res); //3秒后打印出:这里返回一个 Promise });C. onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须变为失败(Rejected),并返回失败的值 e
let promise1 = new Promise((resolve, reject) => { setTimeout(() => { resolve("success"); }, 1000); }); promise2 = promise1.then((res) => { throw new Error("这里抛出一个异常e"); }); promise2.then( (res) => { console.log(res); }, (err) => { console.log(err); //1秒后打印出:这里抛出一个异常e } );D. onFulfilled 不是函数且 promise1 状态为成功(Fulfilled),promise2 必须变为成功(Fulfilled)并返回 promise1 成功的值
let promise1 = new Promise((resolve, reject) => { setTimeout(() => { resolve("success"); }, 1000); }); promise2 = promise1.then("这里的onFulfilled本来是一个函数,但现在不是"); promise2.then( (res) => { console.log(res); // 1秒后打印出:success }, (err) => { console.log(err); } );E. onRejected 不是函数且 promise1 状态为失败(Rejected),promise2 必须变为失败(Rejected) 并返回 promise1 失败的值
let promise1 = new Promise((resolve, reject) => { setTimeout(() => { reject("fail"); }, 1000); }); promise2 = promise1.then((res) => res, "这里的onRejected本来是一个函数,但现在不是"); promise2.then( (res) => { console.log(res); }, (err) => { console.log(err); // 1秒后打印出:fail } );F. resolve 和 reject 结束一个 Promise 的调用 G. catch 方法,捕获异常
其实复杂的是在 then 的异常处理上,不过不用急,边看边理解
1.3.3 方法
-
静态 resolve 方法 参照 1.3.1 用法 2
-
静态 reject 方法 参照 1.3.1 用法 2
-
静态 all 方法 参照 1.3.1 用法 3
-
静态 race 方法 参照 1.3.1 用法 4
-
自定义 done 方法 Promise 对象的回调链,不管以 then 方法或 catch 方法结尾,要是最后一个方法抛出错误,都有可能无法捕捉到(因为 Promise 内部的错误不会冒泡到全局) 因此,我们可以提供一个 done 方法,总是处于回调链的尾端,保证抛出任何可能出现的错误
Promise.prototype.done = function (onFulfilled, onRejected) { this.then(onFulfilled, onRejected).catch(function (reason) { // 抛出一个全局错误 setTimeout(() => { throw reason; }, 0); }); }; -
自定义 finally 方法 finally 方法用于指定不管 Promise 对象最后状态如何,都会执行的操作 它与 done 方法的最大区别,它接受一个普通的回调函数作为参数,该函数不管怎样都必须执行
Promise.prototype.finally = function (callback) { let P = this.constructor; return this.then( (value) => P.resolve(callback()).then(() => value), (reason) => P.resolve(callback()).then(() => { throw reason; }) ); };
1.4 PolyFill
1.4.1 初级版
class MyPromise {
constructor(handle) {
// 判断handle函数与否
if (typeof handle !== "function") {
throw new Error("MyPromise must accept a function as a parameter");
}
// 添加状态
this._status = "PENDING";
// 添加状态
this._value = undefined;
// 执行handle
try {
handle(this._resolve.bind(this), this._reject.bind(this));
} catch (err) {
this._reject(err);
}
}
// 添加 resovle 时执行的函数
_resolve(val) {
if (this._status !== "PENDING") return;
this._status = "FULFILLED";
this._value = val;
}
// 添加 reject 时执行的函数
_reject(err) {
if (this._status !== "PENDING") return;
this._status = "REJECTED";
this._value = err;
}
}
回顾一下,初级版实现了 1,2,3 这三点功能,怎么样还是 so-easy 吧!
1.4.2 中级版
-
由于 then 方法支持多次调用,我们可以维护两个数组,将每次 then 方法注册时的回调函数添加到数组中,等待执行。 在初级的基础上加入成功回调函数队列和失败回调队列和 then 方法
this._fulfilledQueues = []; this._rejectedQueues = []; -
then 方法
then (onFulfilled, onRejected) { const { _value, _status } = this switch (_status) { // 当状态为pending时,将then方法回调函数加入执行队列等待执行 case 'PENDING': this._fulfilledQueues.push(onFulfilled) this._rejectedQueues.push(onRejected) break // 当状态已经改变时,立即执行对应的回调函数 case 'FULFILLED': onFulfilled(_value) break case 'REJECTED': onRejected(_value) break } // 返回一个新的Promise对象 return new MyPromise((onFulfilledNext, onRejectedNext) => { }) } -
then 方法规则完善
// 添加then方法 then (onFulfilled, onRejected) { const { _value, _status } = this // 返回一个新的Promise对象 return new MyPromise((onFulfilledNext, onRejectedNext) => { // 封装一个成功时执行的函数 let fulfilled = value => { try { if (typeof onFulfilled!=='function') { onFulfilledNext(value) } else { let res = onFulfilled(value); if (res instanceof MyPromise) { // 如果当前回调函数返回MyPromise对象,必须等待其状态改变后在执行下一个回调 res.then(onFulfilledNext, onRejectedNext) } else { //否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数 onFulfilledNext(res) } } } catch (err) { // 如果函数执行出错,新的Promise对象的状态为失败 onRejectedNext(err) } } // 封装一个失败时执行的函数 let rejected = error => { try { if (typeof onRejected!=='function') { onRejectedNext(error) } else { let res = onRejected(error); if (res instanceof MyPromise) { // 如果当前回调函数返回MyPromise对象,必须等待其状态改变后在执行下一个回调 res.then(onFulfilledNext, onRejectedNext) } else { //否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数 onFulfilledNext(res) } } } catch (err) { // 如果函数执行出错,新的Promise对象的状态为失败 onRejectedNext(err) } } switch (_status) { // 当状态为pending时,将then方法回调函数加入执行队列等待执行 case 'PENDING': this._fulfilledQueues.push(fulfilled) this._rejectedQueues.push(rejected) break // 当状态已经改变时,立即执行对应的回调函数 case 'FULFILLED': fulfilled(_value) break case 'REJECTED': rejected(_value) break } }) } -
当 resolve 或 reject 方法执行时,我们依次提取成功或失败任务队列当中的函数开始执行,并清空队列,从而实现 then 方法的多次调用
// 添加resovle时执行的函数 _resolve (val) { if (this._status !== PENDING) return // 依次执行成功队列中的函数,并清空队列 const run = () => { this._status = FULFILLED this._value = val let cb; while (cb = this._fulfilledQueues.shift()) { cb(val) } } // 为了支持同步的Promise,这里采用异步调用 setTimeout(() => run(), 0) } // 添加reject时执行的函数 _reject (err) { if (this._status !== PENDING) return // 依次执行失败队列中的函数,并清空队列 const run = () => { this._status = REJECTED this._value = err let cb; while (cb = this._rejectedQueues.shift()) { cb(err) } } // 为了支持同步的Promise,这里采用异步调用 setTimeout(run, 0) } -
当 resolve 方法传入的参数为一个 Promise 对象时,则该 Promise 对象状态决定当前 Promise 对象的状态
// 添加resovle时执行的函数 _resolve (val) { const run = () => { if (this._status !== PENDING) return this._status = FULFILLED // 依次执行成功队列中的函数,并清空队列 const runFulfilled = (value) => { let cb; while (cb = this._fulfilledQueues.shift()) { cb(value) } } // 依次执行失败队列中的函数,并清空队列 const runRejected = (error) => { let cb; while (cb = this._rejectedQueues.shift()) { cb(error) } } /* 如果resolve的参数为Promise对象,则必须等待该Promise对象状态改变后, 当前Promsie的状态才会改变,且状态取决于参数Promsie对象的状态 */ if (val instanceof MyPromise) { val.then(value => { this._value = value runFulfilled(value) }, err => { this._value = err runRejected(err) }) } else { this._value = val runFulfilled(val) } } // 为了支持同步的Promise,这里采用异步调用 setTimeout(run, 0) } -
catch 方法
// 添加catch方法 catch (onRejected) { return this.then(undefined, onRejected) }
1.4.3 高级版
-
静态 resolve 方法
// 添加静态resolve方法 static resolve (value) { // 如果参数是MyPromise实例,直接返回这个实例 if (value instanceof MyPromise) return value return new MyPromise(resolve => resolve(value)) } -
静态 reject 方法
// 添加静态reject方法 static reject (value) { return new MyPromise((resolve ,reject) => reject(value)) } -
静态 all 方法
// 添加静态 all 方法 static all (list) { return new MyPromise((resolve, reject) => { /** * 返回值的集合 */ let values = [] let count = 0 for (let [i, p] of list.entries()) { // 数组参数如果不是MyPromise实例,先调用MyPromise.resolve this.resolve(p).then(res => { values[i] = res count++ // 所有状态都变成fulfilled时返回的MyPromise状态就变成fulfilled if (count === list.length) resolve(values) }, err => { // 有一个被rejected时返回的MyPromise状态就变成rejected reject(err) }) } }) } -
静态 race 方法
// 添加静态 race 方法 static race (list) { return new MyPromise((resolve, reject) => { for (let p of list) { // 只要有一个实例率先改变状态,新的MyPromise的状态就跟着改变 this.resolve(p).then(res => { resolve(res) }, err => { reject(err) }) } }) } -
done 方法 作用:不管以 then 方法或 catch 方法结尾,要是最后一个方法抛出错误,都有可能无法捕捉到(因为 Promise 内部的错误不会冒泡到全局);处于回调链的尾端,保证抛出任何可能出现的错误 目前 Promise 上还没有 done,我们可以自定义一个
Promise.prototype.done = function (onFulfilled, onRejected) { console.log("done"); this.then(onFulfilled, onRejected).catch((reason) => { // 抛出一个全局错误 setTimeout(() => { throw reason; }, 0); }); }; Promise.resolve("这是静态方法的第一个 resolve 值") .then(() => { return "这是静态方法的第二个 resolve 值"; }) .then(() => { throw "这是静态方法的第一个 reject 值"; return "这是静态方法的第二个 resolve 值"; }) .done(); -
finally 方法 作用:不管 Promise 对象最后状态如何,都会执行的操作 与 done 方法的最大区别,它接受一个普通的回调函数作为参数,该函数不管怎样都必须执行
finally (cb) { return this.then( value => MyPromise.resolve(cb()).then(() => value), reason => MyPromise.resolve(cb()).then(() => { throw reason }) ); };
2.1 Generator 定义
- Generator 可以理解为一个状态机,内部封装了很多状态,同时返回一个迭代器 Iterator 对象;
- 迭代器 Iterator 对象:定义标准方式产生一个有限或无限序列值,迭代器有
next()对象; - 多次返回可以被 next 多次调用,最大特点是可以控制执行顺序;
2.2 声明方法
是一种特殊的函数
function* gen(x) {
const y = yield x + 6;
return y;
}
// yield 如果用在另外一个表达式中,要放在()里面
// 像上面如果是在=右边就不用加()
function* genOne(x) {
const y = `这是第一个 yield 执行:${yield x + 1}`;
return y;
}
整个 Generator 函数就是一个封装的异步任务,或者说是异步任务的容器。异步操作需要暂停的地方,都用 yield 语句注明
2.3 执行
-
普通执行
const g = gen(1); //执行 Generator 会返回一个Object,而不是像普通函数返回 return 后面的值 g.next(); // { value: 7, done: false } //调用指针的 next 方法,会从函数的头部或上一次停下来的地方开始执行,直到遇到下一个 yield 表达式或 return 语句暂停,也就是执行yield 这一行 // 执行完成会返回一个 Object, // value 就是执行 yield 后面的值, done 表示函数是否执行完毕 g.next(); // { value: undefined, done: true } // 因为最后一行 return y 被执行完成,所以 done 为 true -
next 方法传参数
const g = gen(1); g.next(); // { value: 7, done: false } g.next(2); // { value: 2, done: true } // next 的参数是作为上个阶段异步任务的返回结果 -
嵌套执行 必须用到
yield*关键字function* genTwo(x) { yield* gen(1); yield* genOne(1); const y = `这是第 二个 yield 执行:${yield x + 2}`; return y; } const iterator = genTwo(1); // 因为 Generator 函数运行时生成的是一个 Iterator 对象,所以可以直接使用 for...of 循环遍历 for (let value of iterator) { console.log(value); // 7 2 3 }
2.4 yield 和 return 的区别
相同点:
- 都能返回语句后面的那个表达式的值
- 都可以暂停函数执行
区别:
- 一个函数可以有多个 yield,但是只能有一个 return
- yield 有位置记忆功能,return 没有
2.5 throw
抛出错误,可以被 try...catch...捕捉到
g.throw('这是抛出的一个错误');
// 这是抛出的一个错误
2.6 应用
// 要求:函数 valOne,valTwo,valThree 依次执行
function* someTask() {
try {
const valOne = yield 1;
const valTwo = yield 2;
const valThree = yield 3;
return "fhf";
} catch (e) {}
}
scheduler(someTask());
function scheduler(task) {
const taskObj = task.next(task.value);
console.log(taskObj); // {value: 1, done: false} {value: 2, done: false} {value: 3, done: false}{value: "fhf", done: true}
// 如果 Generator 函数未结束,就继续调用
if (!taskObj.done) {
task.value = taskObj.value;
scheduler(task);
}
}
2.7 PolyFill
原理图
2.7.1 初级版
实现一个迭代器(Iterator)
// 源码实现
function createIterator(items) {
var i = 0;
return {
next: function () {
var done = i >= items.length;
var value = !done ? items[i++] : undefined;
return {
done: done,
value: value,
};
},
};
}
// 应用
const iterator = createIterator([1, 2, 3]);
console.log(iterator.next()); // {value: 1, done: false}
console.log(iterator.next()); // {value: 2, done: false}
console.log(iterator.next()); // {value: 3, done: false}
console.log(iterator.next()); // {value: undefined, done: true}
2.7.2 中级版
实现可迭代(Iterable)
-
可以通过
for...of...遍历的对象,即原型链上有Symbol.iterator属性; -
Symbol.iterator:返回一个对象的无参函数,被返回对象符合迭代器协议; -
for...of接受一个可迭代对象(Iterable),或者能强制转换/包装成一个可迭代对象的值(如’abc’),遍历时,for...of会获取可迭代对象的Symbol.iterator,对该迭代器逐次调用next(),直到迭代器返回对象的 done 属性为 true 时,遍历结束,不对该 value 处理;const a = ["a", "b", "c", "d", "e"]; for (let val of a) { console.log(val); } // 'a' 'b' 'c' 'd' 'e' // 等价于 const a = ["a", "b", "c", "d", "e"]; for (let val, ret, it = a[Symbol.iterator](); (ret = it.next()) && !ret.done; ) { val = ret.value; console.log(val); } // "a" "b" "c" "d" "e" -
yield*可返回一个 Iterable 对象; -
源码改造
function createIterator(items) { let i = 0; return { next: function () { let done = i >= items.length; let value = !done ? items[i++] : undefined; return { done: done, value: value, }; }, [Symbol.iterator]: function () { return this; }, }; } let iterator = createIterator([1, 2, 3]); console.log(...iterator); // 1, 2, 3
2.7.3 高级版
for...of...接收可迭代对象,能强制转换或包装可迭代对象的值;- 遍历时,
for...of会获取可迭代对象的Symbol.iterator,对该迭代器逐次调用next(),直到迭代器返回对象的 done 属性为 true 时,遍历结束,不对该 value 处理; - 所以可以利用
for...of...封装到原型链上.
Object.prototype[Symbol.iterator] = function* () {
for (const key in this) {
if (this.hasOwnProperty(key)) {
yield [key, this[key]];
}
}
};
3.1 async 作用
-
async 函数返回的是一个 Promise 对象 在函数中 return 一个直接量,async 会把这个直接量通过
Promise.resolve()封装成 Promise 对象async function testAsync() { return "hello async"; } const result = testAsync(); console.log(result); //Promise 对象 -
async 和 then async 返回一个 Promise,所以可以通过 then 获取值
testAsync().then((v) => { console.log(v); // 输出 hello async });所以 async 里面的函数会马上执行,这个就类似 Generator 的
*
3.2 await 作用
-
await 后面可以是 Promise 对象或其他表达式
function getSomething() { return "something"; } async function testAsync() { return Promise.resolve("hello async"); } async function test() { const v1 = await getSomething(); const v2 = await testAsync(); console.log(v1, v2); //something 和 hello async } test(); -
await 后面不是 Promise 对象,直接执行
-
await 后面是 Promise 对象会阻塞后面的代码,Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果
-
所以这就是 await 必须用在 async 的原因,async 刚好返回一个 Promise 对象,可以异步执行阻塞
3.3 async 和 await 结合作用
-
主要是处理 Promise 的链式回调或函数的地狱回调 回到 Generator 中要求函数 valOne,valTwo,valThree 函数依次执行
function valOne() {} function valTwo() {} function valThree() {} async () => { await valOne(); await valTwo(); await valThree(); }; -
处理异常
try...catch...或者await .catch()
3.4 和 Generator 的区别
-
async 是内置执行器(Generator 函数的执行必须依靠执行器),无需手动执行
next() -
更广的适用性。co 模块约定,yield 命令后面只能是 Thunk 函数或 Promise 对象,而 await 后面可以是任意表达式,都会返回一个 Promise 对象
// Thunk 函数:是能将执行结果传入回调函数,并将该回调函数返回的函数 function f(m) { return m * 2; } f(x + 5); // 等同于 var thunk = function () { return x + 5; }; function f(thunk) { return thunk() * 2; } -
返回 Promise,而 Generator 返回 Iterator
-
async 函数就是 Generator 函数的语法糖 async 就相当于 Generator 的
*,await 相当于yield,用法有很多相似之处
3.5 执行器 PolyFill
实现执行器两种方式:
- 回调函数(Thunk 函数)
- Promise 对象
3.5.1 初级版
async function fn(args) {
// ...
}
// 等价于
function fn(args) {
return spawn(function* () {
// ...
});
}
function spawn(gen) {
let g = gen();
function next(data) {
let result = g.next(data);
if (result.done) return result.value;
result.value.then(function (data) {
next(data);
});
}
next();
}
3.5.2 高级版
function spawn(genF) {
//spawn 函数就是自动执行器,跟简单版的思路是一样的,多了 Promise 和容错处理
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);
});
});
}
4.1 Promise,Generator,async 和 await 对比
-
代码对比: 场景:假定某个 DOM 元素上面,部署了一系列的动画,前一个动画结束,才能开始后一个。如果当中有一个动画出错,就不再往下执行,返回上一个成功执行的动画的返回值。
A. Promise
function chainAnimationsPromise(elem, animations) { // 变量 ret 用来保存上一个动画的返回值 let ret = null; // 新建一个空的 Promise let p = Promise.resolve(); // 使用 then 方法,添加所有动画 for (let anim of animations) { p = p.then(function (val) { ret = val; return anim(elem); }); } // 返回一个部署了错误捕捉机制的 Promise return p .catch(function (e) { /* 忽略错误,继续执行 */ }) .then(function () { return ret; }); }B. Generator
function chainAnimationsGenerator(elem, animations) { return spawn(function* () { let ret = null; try { for (let anim of animations) { ret = yield anim(elem); } } catch (e) { /* 忽略错误,继续执行 */ } return ret; }); }C. async & await
async function chainAnimationsAsync(elem, animations) { let ret = null; try { for (let anim of animations) { ret = await anim(elem); } } catch (e) { /* 忽略错误,继续执行 */ } return ret; }
对比可以看出async...await...代码更优雅
4.2 原理
async 和 await 是在 Generator 的基础上封装了自执行函数和一些特性; 具体对比见每个 API 的 PolyFill
4.3 执行顺序
来道头条的面试
async function async1() {
await async2();
console.log("async1 end");
}
async function async2() {
console.log("async2 end");
}
console.log("script start");
async1();
setTimeout(function () {
console.log("setTimeout");
}, 0);
new Promise((resolve) => {
console.log("Promise");
resolve();
})
.then(function () {
console.log("promise1");
})
.then(function () {
console.log("promise2");
});
console.log("script end");
// 旧版 Chrome 打印
// script start => async2 end => Promise => script end => promise1 => promise2 => async1 end => setTimeout
// 新版 Chrome 打印
// script start => async2 end => Promise => script end => async1 end => promise1 => promise2 => setTimeout
// 这里面其他的顺序除了 async1 end , promise1 , promise2 这几个顺序有点争议,其他应该没有什么问题
// 新版是因为 V8 团队将最新的规范进行了修改,await 变得更快了,这道题细节分析不再赘述,上面原理都有讲到
转载分析:
如果 async 关键字函数返回的不是 promise,会自动用
Promise.resolve()包装
javascript 自上而下依次执行:
- async1 函数声明
- async2 函数声明
- 打印"script start"
- 执行 async1 函数
- 执行 async2 函数
- 打印"async2 end"
- await async2 函数,阻塞代码,不继续执行
- 执行 async2 函数
- 执行 setTimeout 函数,放入宏任务队列待执行
- 执行 new Promise
- 打印"Promise"
- 执行 then 函数,放入微任务队列待执行
- 打印"script end"
- 执行 await 阻塞代码。带有 async 关键字的函数仅仅是把 return 值包装成了 promise,这里的
await Promise.resolve()就类似于Promise.resolve(undefined).then((undefined) => {})。所以将(undefined) => {}放入微任务待执行。 - 打印"async1 end"
- 第一轮宏任务执行完毕,执行微任务队列。先进先出,依次执行
console.log("promise1");,console.log("promise2");,(undefined) => {} - 执行宏任务队列中的
console.log("setTimeout");