Promise
相干面试题除去手写题(相干内容参考手写Promise
),最常考的就是输入后果程序。
该局部为Promise
输入后果相干易错点的了解和解析。
async/await相干输入
async function f1() {
console.log(1)
await f2()
console.log(3)
}
async function f2() {
console.log(2)
}
Promise.resolve().then(()=>{
console.log(4)
})
f1()
console.log('start')
// 最初输入为:1 2 start 4 3
如果没有async/await,只有Promise,这里了解起来会容易很多。因而这里易错点分两局部:
- 如何将async/await本义为Promise表达式
new Promise(fn)
中fn
函数何时执行
async/await 的本义
愿函数
async function f1() {
console.log(1)
await f2()
console.log(3)
}
async function f2() {
console.log(2)
}
本义后
function f1() {
return new Promise((resolve)=>{
console.log(1)
const p = f2()
p.then(()=>{
console.log(3)
resolve()
})
})
}
function f2() {
return new Promise((resolve)=>{
console.log(2)
resolve()
})
}
f1() // 1 2 3
本义后的代码,执行f1时,顺着Promise的链路,很容易判断输入后果是1 2 3
new Promise(fn)
中fn
函数何时执行
之前我的项目中有依照Promise A+协定实现自定义的Promise(参考地址),次要看这么一段:
class MyPromise {
status = statusEnum.PENDING;
resArr = [];
rejArr = [];
value = null;
reason = null;
constructor(fn) {
const resolve = (val) => {
// ...
};
const reject = (val) => {
// ...
};
try {
fn(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onResolve, onReject) {
// ...
}
catch(onReject) {
// ...
}
}
能够看到,new Promise(fn)
时,fn
在constructor
中立刻执行,即能够将其看作同步代码。再看之前的示例,联合本义后的后果,输入更容易了解
async function f1() {
console.log(1) // 在f1返回的Promise对象的construstor中立刻执行
await f2()
console.log(3) // 塞入微工作
}
async function f2() {
console.log(2) // 在f2返回的Promise对象的construstor中立刻执行
}
Promise.resolve().then(()=>{ // 塞入微工作
console.log(4)
})
f1() // 先输入立刻执行的后果 1 2
console.log('start') // 输入立刻执行的后果 start
// 执行微工作阶段,此时微工作队列为[()=>{console.log(4)}, ()=>{console.log(3)}]
// 执行微工作队列,输入 4 3
宏工作和js
线程工夫片
Promise.resolve().then(()=>{
setTimeout(()=>{
console.log(2)
})
})
setTimeout(()=>{
Promise.resolve().then(()=>{
console.log(1)
})
})
// 输入 1 2
剖析以上代码,先看第一个宏工作阶段执行的内容
// 假如存在pushToNextMacroTask办法,将function推入下一个宏工作执行
// 第一个宏工作阶段
const microTask = []
microTask.push(()=>{
setTimeout(()=>{
console.log(2)
})
})
pushToNextMacroTask(()=>{
Promise.resolve().then(()=>{
console.log(1)
})
}, 0)
// 执行第一个宏工作的微工作阶段
microTask.forEach(func => func())
/**
* 此时执行了如下内容
* pushToNextMacroTask(()=>{
console.log(2)
}, 0)
*/
这里开始存在一个分歧点,第一个宏工作阶段,别离执行了两次pushToNextMacroTask
,将办法推至下一个宏工作,那么下一个宏工作是否是同一个宏工作?对该局部代码打Performance
快照后,后果如下:
能够发现,尽管两个setTimeout
定时的工夫都是0,但并不会将工作放到同一个宏工作中。最终后果解析如下:
Promise.resolve().then(()=>{
setTimeout(()=>{
console.log(2)
})
})
setTimeout(()=>{
Promise.resolve().then(()=>{
console.log(1)
})
})
/**
* 假如宏工作列表:macroTaskList = []
* 假如微工作列表:microTaskList = []
* 1. 第一个宏工作阶段
* 执行Promise.resolve().then,将其回调塞入微工作队列
* microTaskList = [
* ()=>{
* setTimeout(()=>{
* console.log(2)
* })
* }
* ]
* 执行setTimeout, 将其回调函数放入下一个宏工作
* macroTaskList = [
* ()=>{console.log(1)}
* ]
* 1.1 第一个宏工作的微工作阶段
* const microTask = macroTaskList.shift()
* microTask()
* 执行setTimeout,将其回调塞入下一个宏工作
* macroTaskList = [
* ()=>{console.log(1)},
* ()=>{console.log(2)}
* ]
* 2. 第二个宏工作阶段
* const macroTask = macroTaskList/shift()
* macroTask() // 输入1
* 2.2 第二个宏工作的微工作阶段,没有微工作
* 3. 第三个宏工作阶段
* const macroTask = macroTaskList/shift()
* macroTask() // 输入2
*
* 总结:
* 输入 1 2
*/
js
线程和渲染线程互斥,浏览器调配给js
线程执行的一个工夫片中,蕴含多个宏工作。先后执行两次setTimeout(fn, 0)
,两个fn
会别离放入两个宏工作中,而非同一个宏工作。
发表回复