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)时,fnconstructor中立刻执行,即能够将其看作同步代码。再看之前的示例,联合本义后的后果,输入更容易了解

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 2console.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会别离放入两个宏工作中,而非同一个宏工作。