天天看點

Promise的面試題

一.基礎篇

1.1

const promise1 = new Promise((resolve, reject) => {
  console.log('promise1')
})
console.log('1', promise1);

           

分析:

  • 從上至下,先遇到new Promise,執行該構造函數中的代碼promise1(此代碼是同步的)
  • 然後執行同步代碼1,此時promise1沒有被resolve或者reject,是以狀态還是pending

結果:

‘promise1’

‘1’ Promise{}

1.2

const promise = new Promise((resolve, reject) => {
  console.log(1);
  resolve('success')
  console.log(2);
});
promise.then(() => {
  console.log(3);
});
console.log(4);

           

分析:

  • 從上至下,先遇到new Promise,執行其中的同步代碼1
  • 再遇到resolve(‘success’), 将promise的狀态改為了resolved并且将值儲存下來
  • 繼續執行同步代碼2
  • 跳出promise,往下執行,碰到promise.then這個微任務,将其加入微任務隊列
  • 執行同步代碼4
  • 本輪宏任務全部執行完畢,檢查微任務隊列,發現promise.then這個微任務且狀态為resolved,執行它。

結果:

1 2 4 3

1.3

const promise = new Promise((resolve, reject) => {
  console.log(1);
  console.log(2);
});
promise.then(() => {
  console.log(3);
});
console.log(4);

           

分析:

和題目二相似,隻不過在promise中并沒有resolve或者reject

是以promise.then并不會執行,它隻有在被改變了狀态之後才會執行。

結果:

1 2 4

1.4

const promise1 = new Promise((resolve, reject) => {
  console.log('promise1')
  resolve('resolve1')
})
const promise2 = promise1.then(res => {
  console.log(res)
})
console.log('1', promise1);
console.log('2', promise2);

           

分析:

從上至下,先遇到new Promise,執行該構造函數中的代碼promise1

碰到resolve函數, 将promise1的狀态改變為resolved, 并将結果儲存下來

碰到promise1.then這個微任務,将它放入微任務隊列

promise2是一個新的狀态為pending的Promise

執行同步代碼1, 同時列印出promise1的狀态是resolved

執行同步代碼2,同時列印出promise2的狀态是pending

宏任務執行完畢,查找微任務隊列,發現promise1.then這個微任務且狀态為resolved,執行它。

結果:

‘promise1’

‘1’ Promise{: ‘resolve1’}

‘2’ Promise{}

‘resolve1’

二.結合setTimeout

1.1

console.log('start')
setTimeout(() => {
  console.log('time')
})
Promise.resolve().then(() => {
  console.log('resolve')
})
console.log('end')

           

分析:

剛開始整個腳本作為一個宏任務來執行,對于同步代碼直接壓入執行棧進行執行,是以先列印出start和end。

setTimout作為一個宏任務被放入宏任務隊列(下一個)

Promise.then作為一個微任務被放入微任務隊列

本次宏任務執行完,檢查微任務,發現Promise.then,執行它

接下來進入下一個宏任務,發現setTimeout,執行。

結果:

‘start’

‘end’

‘resolve’

‘time’

1.2

const promise = new Promise((resolve, reject) => {
  console.log(1);
  setTimeout(() => {
    console.log("timerStart");
    resolve("success");
    console.log("timerEnd");
  }, 0);
  console.log(2);
});
promise.then((res) => {
  console.log(res);
});
console.log(4);

           

分析:

從上至下,先遇到new Promise,執行該構造函數中的代碼1

然後碰到了定時器,将這個定時器中的函數放到下一個宏任務的延遲隊列中等待執行

執行同步代碼2

跳出promise函數,遇到promise.then,但其狀态還是為pending,這裡了解為先不執行

執行同步代碼4

一輪循環過後,進入第二次宏任務,發現延遲隊列中有setTimeout定時器,執行它

首先執行timerStart,然後遇到了resolve,将promise的狀态改為resolved且儲存結果并将之前的promise.then推入微任務隊列

繼續執行同步代碼timerEnd

宏任務全部執行完畢,查找微任務隊列,發現promise.then這個微任務,執行它。

結果:

結果就不給啦,自己去試試吧

1.3

Promise.resolve().then(() => {
  console.log('promise1');
  const timer2 = setTimeout(() => {
    console.log('timer2')
  }, 0)
});
const timer1 = setTimeout(() => {
  console.log('timer1')
  Promise.resolve().then(() => {
    console.log('promise2')
  })
}, 0)
console.log('start');

           

分析:

剛開始整個腳本作為第一次宏任務來執行,我們将它标記為宏1,從上至下執行

遇到Promise.resolve().then這個微任務,将then中的内容加入第一次的微任務隊列标記為微1

遇到定時器timer1,将它加入下一次宏任務的延遲清單,标記為宏2,等待執行(先不管裡面是什麼内容)

執行宏1中的同步代碼start

第一次宏任務(宏1)執行完畢,檢查第一次的微任務隊列(微1),發現有一個promise.then這個微任務需要執行

執行列印出微1中同步代碼promise1,然後發現定時器timer2,将它加入宏2的後面,标記為宏3

第一次微任務隊列(微1)執行完畢,執行第二次宏任務(宏2),首先執行同步代碼timer1

然後遇到了promise2這個微任務,将它加入此次循環的微任務隊列,标記為微2

宏2中沒有同步代碼可執行了,查找本次循環的微任務隊列(微2),發現了promise2,執行它

第二輪執行完畢,執行宏3,列印出timer2

結果:

‘start’

‘promise1’

‘timer1’

‘promise2’

‘timer2’

promise中的then,catch

1.1

const promise = new Promise((resolve, reject) => {
  resolve("success1");
  reject("error");
  resolve("success2");
});
promise
.then(res => {
    console.log("then: ", res);
  }).catch(err => {
    console.log("catch: ", err);
  })

           

分析:

構造函數中的 resolve 或 reject 隻有第一次執行有效,多次調用沒有任何作用 。

結論:Promise的狀态一經改變就不能再改變。

是以答案是什麼呢?

1.2

const promise = new Promise((resolve, reject) => {
  reject("error");
  resolve("success2");
});
promise
.then(res => {
    console.log("then1: ", res);
  }).then(res => {
    console.log("then2: ", res);
  }).catch(err => {
    console.log("catch: ", err);
  }).then(res => {
    console.log("then3: ", res);
  })

           

結果:

"catch: " “error”

"then3: " undefined

分析:

至于then3也會被執行,那是因為catch()也會傳回一個Promise,且由于這個Promise沒有傳回值,是以列印出來的是undefined。

1.3

Promise.resolve(1)
  .then(res => {
    console.log(res);
    return 2;
  })
  .catch(err => {
    return 3;
  })
  .then(res => {
    console.log(res);
  });

           

結果: 1 2

分析:

Promise可以鍊式調用,不過promise 每次調用 .then 或者 .catch 都會傳回一個新的 promise,進而實作了鍊式調用, 它并不像一般我們任務的鍊式調用一樣return this。

上面的輸出結果之是以依次列印出1和2,那是因為resolve(1)之後走的是第一個then方法,并沒有走catch裡,是以第二個then中的res得到的實際上是第一個then的傳回值。

且return 2會被包裝成resolve(2)。

async and await

1.1

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

           

注:這裡首先要明白async和promise的關系,明白用promise形式是如何書寫代碼的哦,可以檢視async和await的簡介

上面代碼等同于

// 等同于

    function async1(){
        return new Promise((resolve,reject)=> {
            console.log("async1 start");
            async2().tnen(res => {
                console.log("async1 end");
                resolve()
            })
        })
    }

    function async2(){
        return new Promise((resolve,reject) => {
            console.log("async2");
        })
    }
    async1()
    console.log('start')
           

這下知道結果是什麼了吧

1.2

async function async1() {
  console.log("async1 start");
  await async2();
  console.log("async1 end");
}
async function async2() {
  setTimeout(() => {
    console.log('timer')
  }, 0)
  console.log("async2");
}
async1();
console.log("start")

           

結果:

‘async1 start’

‘async2’

‘start’

‘async1 end’

‘timer’

1.3

async function async1() {
  console.log("async1 start");
  await async2();
  console.log("async1 end");
  setTimeout(() => {
    console.log('timer1')
  }, 0)
}
async function async2() {
  setTimeout(() => {
    console.log('timer2')
  }, 0)
  console.log("async2");
}
async1();
setTimeout(() => {
  console.log('timer3')
}, 0)
console.log("start")

           

哈哈哈,有沒有繞暈你呀,自己去解鎖吧