前端面试题系列之promise相关

123 阅读4分钟

题目一

async function test1() {
	console.log('test1 start')
	await test2()
	console.log('test2 end')
	await test3()
	console.log('test3 end')
}

async function test2() {
	console.log('test2 start')
}

async function test3() {
	console.log('test3 start')
}

console.log('script start')
test1()
console.log('script end')

考察内容:async await的执行顺序
理解:
    1. 执行async函数,返回的是Promise对象;await 相当于Promise的then方法
    2. promise初始化函数会立即执行,相当于同步;而promise.then相当于异步
    
就如同这个代码
async function test1() {
    console.log('test1 start')
    await test2()
    console.log('test2 end') 
}
async function test2() {
    console.log('test2 start') 
}
test1()
等同于下面这个
new Promise((resolve,reject) => {
    console.log('test1 start')
    new Promise((resolve,reject) => {
            console.log('test2 start') 
    })
    resolve()
}).then(() => {
    console.log('test2 end')
})

根据以上理解,可以简单的得出代码的执行顺序了
先同步,后异步。从上往下,依次执行

同步代码
console.log('script start')
console.log('test1 start')
console.log('test2 start')
console.log('script end')

异步代码
console.log('test2 end')
console.log('test3 start')
console.log('test3 end')

题目二

async function async1 () {
	console.log('async1 start') 
	await async2()
	console.log('async1 end') 
}

async function async2 () {
	console.log('async2') 
}

console.log('script start')

setTimeout(function () { 
	console.log('setTimeout')
}, 0)

async1()

// 初始化promise时,传入的函数会被立刻执行
new Promise (function (resolve) { 
	console.log('promise1')
	resolve()
}).then (function () { 
	console.log('promise2')
})

console.log('script end')

考察内容: 
    1. 同步和异步的执行顺序
    2. promise初始化和promise.then的执行顺序
    3. setTimeout和promise的执行顺序
    4. async await 和promise的关系

回想一下,这些问题都了如指掌的话,那么这个题目也就非常简单了。

理解:
    1. 同步先执行完成,再执行异步
    2. promise初始化函数会立即执行,相当于同步。而promise.then相当于异步
    3. setTimeout是宏任务,promise是微任务,微任务先于宏任务执行
    4. 执行async函数,返回的是Promise对象;await 相当于Promise的then方法;try..catch 用来捕获异常,替代了Promisecatch


根据以上理解,从上往下执行过程中,会执行那些同步代码?异步代码又是哪些?

先执行的同步代码有
console.log('script start')
async1函数中的 console.log('async1 start')
async2函数中的 console.log('async2')
new Promise中的 console.log('promise1')
console.log('script end')

等同步代码执行完成后,再执行异步代码。从上往下,先微任务,后宏任务
async1函数中的 console.log('async1 end')
new Promise中的 console.log('promise2')
setTimeout中的 console.log('setTimeout')

题目三

Promise.resolve().then(() => {
    console.log(0)
    return Promise.resolve(4)
}).then((res) => {
    console.log(res)
})

Promise.resolve().then(() => {
    console.log(1)
}).then(() => {
    console.log(2)
}).then(() => {
    console.log(3)
}).then(() => {
    console.log(5)
}).then(() => {
    console.log(6)
})

考察内容:promise.then执行问题

理解:
    1. then交替执行
        1. 如果有多个fulfilled promise 实例,同时执行then链式调用
        2. then会交替执行
        3. 这是编辑器的优化,防止一个promise占据太久时间
    2. then中返回的新的promise实例
        1. 也会遵循then交替执行
        2. 但会出现“慢两拍”效果
        3. 第一拍,新的promise需要由pending变为fulfilled,此时另一条链会执行一次then方法
        4. 第二拍,新的promise的then方法挂载到MicroTaskQueue中,此时另一条链会再执行一次then方法

证实:
1, then交替执行
 Promise.resolve().then(() => {
    console.log(1)
}).then(() => {
    console.log(3)
})
Promise.resolve().then(() => {
    console.log(2)
}).then(() => {
    console.log(4)
})
从上往下,依次交替执行,输出结果是: 1 2 3 4

2. then中返回的新的promise实例
Promise.resolve().then(() => {
    console.log(10);
    return Promise.resolve(20)
}).then((res) => {
    console.log(res)
}).then(() => {
    console.log(30)
}).then(() => {
    console.log(40)
}).then(() => {
    console.log(50)
})
Promise.resolve().then(() => {
    console.log(1)
}).then(() => {
    console.log(2)
}).then(() => {
    console.log(3)
}).then(() => {
    console.log(4)
}).then(() => {
    console.log(5)
})
根据第一步证实的then交替执行,应该会出现这样的输出结果: 10 1 20 2 30 3 40 4 50 5
但是then中返回新的promise会慢两拍,因此实际输出的结果: 10 1 2 3 20 4 30 5  40 50
可以看出20的位置从第三位变成了第五位,比预想的慢了两拍


根据以上理解,可以简单的得出题目中代码的执行顺序了
第一次交替
1. 微任务队列:() => console.log(0)、() => console.log(1)
2. 执行打印:0 1
第二次交替
1. 微任务队列: Promise.resolve(4)【pendding => resolve】、() => console.log(2)
2. 执行打印:2
第三次交替
1. 微任务队列:Promise.resolve(4)【will add to microTaskQueue】、() => console.log(3)
2. 执行打印:3
第四次交替
1. 微任务队列:(res) => console.log(res)、() => console.log(5)
2. 执行打印:4 5
第五次交替
1. 微任务队列:() => console.log(6)
2. 执行打印:6

总结

  1. async/await 和 Promise 的关系

    1. 执行async函数,返回的是Promise对象
    2. await 相当于Promise的then方法
    3. try..catch 用来捕获异常,替代了Promise的catch
  2. 宏任务和微任务

    1. 宏任务: setTimeout,setInterval, Ajax, DOM事件,setImmediate,I/O(Node.js)

    2. 微任务: Promise.then,MutaionObserve,process.nextTick,async/await

    3. 注意: 定时器的时间是指从执行定时器开始计时的,当时间到了之后就会把这个任务放到Callback Queue中

    4. 二者的执行时间:微任务 --》 DOM渲染 --》 宏任务

    5. 执行顺序是:

      1. Call Stack清空
      2. 执行当前的微任务
      3. 尝试渲染DOM
      4. 触发event loop机制
  3. promise.then执行问题

    1. then交替执行

      1. 如果有多个fulfilled promise 实例,同时执行then链式调用
      2. then会交替执行
      3. 这是编辑器的优化,防止一个promise占据太久时间
    2. then中返回新的promise实例

      1. 也会遵循交替执行
      2. 但会出现“慢两拍”的效果
      3. 第一拍,promise需要由pending变为fulfilled
      4. 第二拍,then函数挂载到MicroTaskQueue中去