异步处理
阶段一:回调函数
当异步任务完成,调用对应回调函数(一般有两个:成功回调和失败回调),并将数据或错误以函数参数传入
阶段二:Promise
ES6 新增的 API,目的是为了解决异步处理中回调函数方式的一些问题:
- 回调地狱:回调函数层层嵌套(回调套回调),代码难以阅读和维护
- 控制反转:如不能保证回调函数被正确调用(一个回调可能被调用多次、成功回调和失败回调都被调用)
- 规则 VS. 规范
Promise
如何解决?
.then(...).then(...)
打平,无需嵌套Promise
的状态只能够改变一次(pending → resolved
或pending → rejected
)
Promise
可以完全代替回调函数?
- 不能。需要多次调用的回调函数、事件的回调函数(写成
Promise
很奇怪)
阶段三:Generator
函数
生成器函数可以退出,并在稍后重新进入,其上下文(变量绑定)会在重新进入时保存
function*
声明创建一个 GeneratorFunction
对象。每次调用生成器函数时,它都会返回一个新的 Generator
对象,该对象符合迭代器协议。当迭代器的 next()
方法被调用时,生成器函数的主体会被执行,直到遇到第一个 yield
表达式,该表达式指定了迭代器要返回的值,或者用 yield*
委托给另一个生成器函数。next()
方法返回一个对象,其 value
属性包含了 yield
表达式的值,done
属性是布尔类型,表示生成器是否已经返回了最后一个值。如果 next()
方法带有参数,那么它会恢复生成器函数的执行,并用参数替换暂停执行的 yield
表达式
其本质就是一个状态机
阶段四:async
、await
Promise
+ GeneratorFunction
的语法糖,可以写看起来像同步的代码而完成异步编程
Promise
知识点
new Promise(executor)
中的executor
函数体的代码是同步执行的.then(onfulfilled, onrejected)
中的onfulfilled
和onrejected
回调函数是放入微任务队列,放入时机:- 调用
.then
时Promise
还是pending
:当Promise
完成时(resolve
或reject
)放入 - 调用
.then
时Promise
状态已改变:立即放入
- 调用
- 每调用一个
.then
就产生一个新的Promise
并作为返回值(.then
的链式调用),其状态取决于该then
中回调函数的返回值:- 返回普通值:状态是成功(
resolved
),相当于:Promise.resolve(返回值)
- 返回
Promise
:状态吸收
- 返回普通值:状态是成功(
.catch
同理,它只是.then
的语法糖.finally
也会产生一个新的Promise
并作为返回值,其状态和当前 Promise 状态一致(没有状态吸收)- 其参数回调函数
onFinally
没有参数,返回值也会被忽略
- 其参数回调函数
await xxx
:xxx 同步执行,如果不是一个Promise
则使用Promise.resolve()
包裹- 当该
Promise
完成后把后面所有的执行语句推入到微任务队列 - 如果没有后续语句则推入“函数完成”到微任务队列
- 当该
- 每调用一个
async
函数就产生一个新的Promise
并作为返回值,其状态取决于该函数的返回值:- 返回普通值:状态是成功(
resolved
),相当于:Promise.resolve(返回值)
- 返回
Promise
:状态吸收
- 返回普通值:状态是成功(
async
函数具有传染性,消除传染性:- 调两次,第一次抛出
Promise
,在该Promise
中进行异步操作,在外部捕获该错误,Promise
完成后再次调用函数 - React 的
<Suspense fallback={Loading}>child</Suspense>
就是如此实现的 - https://www.bilibili.com/video/BV1hp4y1A71j/
- 调两次,第一次抛出
状态吸收
Promise 状态吸收是指:
- 一个 Promise 要与另一个 Promise 状态保持一致
- 即:后者的状态决定了前者的状态,前者需要吸收后者的状态
以下三种情况下,涉及 Promise 的状态吸收:
// 情况 1: .then 的参数回调函数返回了一个 Promise(.catch 一样)
const p2 = p.then(() => p1)
// p2 要吸收 p1 的状态
// 情况 2: async 函数返回了一个 Promise
const p4 = await asyncFunc()
async function asyncFunc() {
return p3
}
// p4 要吸收 p3 的状态
// 情况 3: resolve 接收了一个 Promise(reject 一样)
const p6 = new Promise((resolve, reject) => {
resolve(p5)
})
// p6 要吸收 p5 的状态
// 注意:Promise.resolve() 接收 Promise 参数不会状态吸收
const p8 = Promise.resolve(p7)
// 这里的 p8 === p7
// 注意:finally() 返回的新 Promise p10 与 p9 状态一致,但没有状态吸收
const p10 = p9.finally()
Promise A+ 规范没有详细规定如何状态吸收,如何保持状态一致取决于 JS 引擎具体实现,下面说一下 Chrome 和 Node.js 的 V8 的实现方式
V8 将状态吸收分为两个步骤:
- 准备
- 吸收
每一个步骤都是放到微队列中运行
拿上面的情况 1 举例:
- 当
p1
完成后 - 准备:将
p2 状态 = p1 状态
推入微队列,相当于p1.then(() => p2 状态 = p1 状态)
- 吸收:相当于从微队列拿出
p2 状态 = p1 状态
执行,这时p2
完成了且状态与p1
一致
题目
1
async function async1() {
console.log(1);
await async2();
console.log(2);
}
async function async2() {
console.log(3);
}
console.log(4);
setTimeout(function () {
console.log(5);
}, 0)
async1();
new Promise(function (resolve) {
console.log(6);
resolve();
}).then(function () {
console.log(7);
});
console.log(8);
答案
4 1 3 6 8 2 7 5
2
console.log(1);
setTimeout(() => {
console.log(2);
Promise.resolve().then(() => {
console.log(3);
})
}, 0);
new Promise(function (resolve, reject) {
console.log(4);
setTimeout(function () {
console.log(5);
resolve(6);
}, 0);
}).then((res) => {
console.log(7);
setTimeout(() => {
console.log(res);
}, 0);
})
答案
1 4 2 3 5 7 6
3
const p = function() {
return new Promise((resolve, reject) => {
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1);
}, 0);
resolve(2);
});
p1.then((res) => {
console.log(res);
})
console.log(3);
resolve(4);
});
}
p().then((res) => {
console.log(res);
});
console.log ('end');
答案
3 end 2 4
4
const p = function() {
return new Promise((resolve, reject) => {
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1);
}, 0);
// resolve(2);
});
p1.then((res) => {
console.log(res);
})
console.log(3);
resolve(4);
});
}
p().then((res) => {
console.log(res);
});
console. log ('end');
答案
3 end 4 1
5
async function f1() {
console.log(1)
await f2()
console.log(2)
}
f2 = async () => {
await setTimeout(() => {
Promise.resolve().then(() => {
console.log(3)
})
console.log(4)
})
}
f3 = async () => {
Promise.resolve().then(() => {
console.log(6)
})
}
f1()
console.log(7)
f3()
答案
1 7 6 2 4 3
6
async function f1() {
console.log(1)
await f2()
console.log(2)
}
f2 = async () => {
await (async () => {
await (() => {
console.log(3)
})()
console.log(4)
})()
}
f3 = async () => {
Promise.resolve().then(() => {
console.log(6)
})
}
f1()
console.log(7)
f3()
答案
1 3 7 4 6 2
7
const p1 = new Promise((resolve, reject) => {
resolve()
})
const p2 = new Promise((resolve, reject) => {
resolve(p1)
})
p2.then(() => {
console.log('1')
})
.then(() => {
console.log('2')
})
.then(() => {
console.log('3')
})
p1.then(() => {
console.log('4')
})
.then(() => {
console.log('5')
})
.then(() => {
console.log('6')
})
答案
4 5 1 6 2 3
8
async function async1() {
console.log(1)
await async2()
console.log('AAA')
}
async function async2() {
return Promise.resolve(2)
}
async1()
Promise.resolve()
.then(() => console.log(3))
.then(() => console.log(4))
.then(() => console.log(5))
答案
1 3 4 AAA 5
9
async function async1() {
console.log(1)
await async2()
console.log('AAA')
}
function async2() {
return Promise.resolve(2)
}
async1()
Promise.resolve()
.then(() => console.log(3))
.then(() => console.log(4))
.then(() => console.log(5))
答案
1 AAA 3 4 5
10
Promise.resolve().then(() => {
console.log(0)
return Promise.resolve(4)
}).then(res => {
console.log(res)
})
Promise.resolve().then(() => {
console.log(1)
}).then(res => {
console.log(2)
}).then(res => {
console.log(3)
}).then(res => {
console.log(5)
}).then(res => {
console.log(6)
})
答案
0 1 2 3 4 5 6