幾道經常會被問到的Promise面試題

Promise面試題

題目一

const promise = newPromise((resolve, reject) => {

console。log(1);

resolve();

console。log(2);

})

promise。then(() => {

console。log(3);

})

console。log(4);

解析

首先Promise新建後立即執行,所以會先輸出1,2,而Promise。then()內部的程式碼在當次事件迴圈的結尾立即執行,所以會先輸出4,最後輸出3。

答案:1 2 4 3

題目二

const promise = newPromise((resolve, reject) => {

resolve(‘success1’);

reject(‘error’);

resolve(‘success2’);

});

promise。then((res) => {

console。log(‘then:’, res);

})。catch((err) => {

console。log(‘catch:’, err);

})

解析

resolve函式Promise物件的狀態從“未完成”變為“成功”(即從pending變為resolved),在非同步操作成功時呼叫,並將非同步操作的結果,作為引數傳遞出去;

reject函式將Promise物件的狀態從“未完成”變為“失敗”(即從pending變為rejected),在非同步操作失敗時呼叫,並將非同步操作報出的錯誤,作為引數傳遞出去。

而一旦狀態改變,就不會有再變。

所以程式碼中的reject(‘error’);不會有作用。

Promise只能resolve一次,剩下的呼叫都會被忽略。

所以第二次resolve(‘success’);也不會有作用。

答案:then:success1

題目三

Promise。resolve(1)

。then(2)

。then(Promise。resolve(3))

。then(console。log)

解析

Promise。resolve方法的引數如果是一個原始值,或者是一個不具有then方法的物件,則Promise。resolve方法返回一個新的Promise物件,狀態為resolved,Promise。resolve方法的引數,會同時傳給回撥函式。

then方法接受的引數是函式,而如果傳遞的並非是一個函式,它實際上會將其解釋為then(null),這就會導致前一個Promise的結果會傳遞下面。

答案 1

題目四

紅燈3秒亮一次,綠燈1秒亮一次,黃燈2秒亮一次;如何讓三個燈不斷交替重複亮燈?(用Promise實現)三個亮燈函式已經存在:

functionred() {

console。log(‘red’);

}

functiongreen() {

console。log(‘green’);

}

functionyellow() {

console。log(‘yellow’);

}

解析

紅燈3秒亮一次,綠燈1秒亮一次 ,黃燈2秒亮一次,意思就是3秒執行一次red函式,2秒執行一次green函式,1秒執行一次yellow函式,不斷交替重複亮燈,意思就是按照這個順序一直執行這3個函式,這步可以利用遞迴來實現。

答案:

functionred() {

console。log(‘red’);

}

functiongreen() {

console。log(‘green’);

}

functionyellow() {

console。log(‘yellow’);

}

var light = function (timmer, cb) {

returnnewPromise(function (resolve, reject) {

setTimeout(function () {

cb();

resolve();

}, timmer);

});

};

var step = function () {

Promise。resolve()。then(function () {

return light(3000, red);

})。then(function () {

return light(2000, green);

})。then(function () {

return light(1000, yellow);

})。then(function () {

step();

});

}

step();

題目五

實現mergePromise函式,把傳進去的陣列按順序先後執行,並且把返回的資料先後放到陣列data中。

const timeout = ms =>newPromise((resolve, reject) => {

setTimeout(() => {

resolve();

}, ms);

});

const ajax1 = () => timeout(2000)。then(() => {

console。log(‘1’);

return1;

});

const ajax2 = () => timeout(1000)。then(() => {

console。log(‘2’);

return2;

});

const ajax3 = () => timeout(2000)。then(() => {

console。log(‘3’);

return3;

});

const mergePromise = ajaxArray => {

// 在這裡實現你的程式碼

};

mergePromise([ajax1, ajax2, ajax3])。then(data => {

console。log(‘done’);

console。log(data); // data 為 [1, 2, 3]

});

// 要求分別輸出

// 1

// 2

// 3

// done

// [1, 2, 3]

解析

首先ajax1,ajax2,ajax3都是函式,只是這些函式執行後會返回一個Promise,按照題目要求只要順序執行這三個函式就好了,然後把結果放到data中,但是這些函式都是非同步操作,想要按順序執行輸出1,2,3並非那麼容易,舉例

functionA() {

setTimeout(function () {

console。log(‘a’);

}, 3000);

}

functionB() {

setTimeout(function () {

console。log(‘b’);

}, 1000);

}

A();

B();

// b

// a

例子中按照順序執行A,B但是輸出的結果卻是b,a。因為對於非同步函式來說,並不會按順序執行完一個,再執行後一個。

這道題主要考察用Promise控制非同步流程,讓這些涵涵素,一個執行完,再執行下一個。

答案

// 儲存陣列中的函式執行後的結果

var data = [];

// Promise。resolve方法呼叫時不帶引數,直接返回一個resolved狀態的 Promise 物件。

var sequence = Promise。resolve();

ajaxArray。forEach(function (item) {

// 第一次的 then 方法用來執行陣列中的每個函式,

// 第二次的 then 方法接受陣列中的函式執行後返回的結果,

// 並把結果新增到 data 中,然後把 data 返回。

sequence = sequence。then(item)。then(function (res) {

data。push(res);

return data;

});

})

// 遍歷結束後,返回一個 Promise,也就是 sequence, 他的 [[PromiseValue]] 值就是 data,

// 而 data(儲存陣列中的函式執行後的結果) 也會作為引數,傳入下次呼叫的 then 方法中。

return sequence;

題目六

以下程式碼最後輸出什麼

const first = () => (newPromise((resolve, reject) => {

console。log(3);

let p = newPromise((resolve, reject) => {

console。log(7);

setTimeout(() => {

console。log(5);

resolve(6);

}, 0)

resolve(1);

});

resolve(2);

p。then((arg) => {

console。log(arg);

});

}));

first()。then((arg) => {

console。log(arg);

});

console。log(4);

解析

這道題主要理解js執行機制

第一輪事件迴圈

先執行宏任務,主script,new Promise立即執行,輸出 3,

執行p這個new Promise操作,輸出 7,

發現setTimeout,將回調函式放入下一輪任務佇列(Event Quene),p的then,暫且命名為then1,放入微任務佇列,且first也有then,命名為then2,放入微任務佇列。執行console。log(4),輸出 4,宏任務執行結束。

再執行微任務,執行then1,輸出 1,

執行then2,輸出 3。

第一輪事件迴圈結束,開始執行第二輪。

第二輪事件迴圈

先執行宏任務裡面的,也就是setTimeout的回撥,輸出 5。

resolve(6)不會生效,因為p的Promise狀態一旦改變就不會再變化了。

答案 3 7 4 1 2 5

題目七

有8個圖片資源的url,已經儲存在陣列urls中(即urls=[‘http://example。com/1。jpg’,…,‘http:’‘example。com/8。jpg’]),而且已經有一個函式function loading,輸入一個url連結,返回一個Promise,該Promise在圖片下載完成的時候resolve,下載失敗則reject。

但有一個要求,任何時刻同時下載的連結數量不可以超過3個。

請寫一段程式碼實現這個需求,要求儘可能快速地將所有圖片下載完成。

var urls = [‘https://www。kkkk1000。com/images/getImgData/getImgDatadata。jpg’, ‘https://www。kkkk1000。com/images/getImgData/gray。gif’, ‘https://www。kkkk1000。com/images/getImgData/Particle。gif’, ‘https://www。kkkk1000。com/images/getImgData/arithmetic。png’, ‘https://www。kkkk1000。com/images/getImgData/arithmetic2。gif’, ‘https://www。kkkk1000。com/images/getImgData/getImgDataError。jpg’, ‘https://www。kkkk1000。com/images/getImgData/arithmetic。gif’, ‘https://www。kkkk1000。com/images/wxQrCode2。png’];

function​loadImg(url) {

returnnewPromise((resolve, reject) => {

const img = new Image()

img。on​load = function () {

console。log(‘一張圖片載入完成’);

resolve();

}

img。on​error = reject

img。src = url

})

};

解析

題目的意思是需要先併發請求3張圖片,當一張圖片載入完成後,又會繼續發起一張圖片的請求,讓併發數保持在3個,直到需要載入的圖片都全部發起請求。

用Promise來實現就是,先併發請求3個圖片資源,這樣可以得到3個Promise,組成一個數組promises,然後不斷呼叫Promise。race來返回最快改變狀態的Promise,然後從陣列promises中刪掉這個Promise物件,再加入一個新的Promise,直到全部的url被取完,最後再使用Promise。all來處理一遍陣列promises中沒有改變狀態的Promise

答案

var urls = [‘https://www。kkkk1000。com/images/getImgData/getImgDatadata。jpg’, ‘https://www。kkkk1000。com/images/getImgData/gray。gif’, ‘https://www。kkkk1000。com/images/getImgData/Particle。gif’, ‘https://www。kkkk1000。com/images/getImgData/arithmetic。png’, ‘https://www。kkkk1000。com/images/getImgData/arithmetic2。gif’, ‘https://www。kkkk1000。com/images/getImgData/getImgDataError。jpg’, ‘https://www。kkkk1000。com/images/getImgData/arithmetic。gif’, ‘https://www。kkkk1000。com/images/wxQrCode2。png’];

function​loadImg(url) {

returnnewPromise((resolve, reject) => {

const img = new Image()

img。on​load = function () {

console。log(‘一張圖片載入完成’);

resolve();

}

img。on​error = reject

img。src = url

})

};

functionlimitLoad(urls, handler, limit) {

// 對陣列做一個複製

const sequence = []。concat(urls)

let promises = [];

//併發請求到最大數

promises = sequence。splice(0, limit)。map((url, index) => {

// 這裡返回的 index 是任務在 promises 的腳標,用於在 Promise。race 之後找到完成的任務腳標

return handler(url)。then(() => {

return index

});

});

// 利用陣列的 reduce 方法來以佇列的形式執行

return sequence。reduce((last, url, currentIndex) => {

return last。then(() => {

// 返回最快改變狀態的 Promise

returnPromise。race(promises)

})。catch(err => {

// 這裡的 catch 不僅用來捕獲 前面 then 方法丟擲的錯誤

// 更重要的是防止中斷整個鏈式呼叫

console。error(err)

})。then((res) => {

// 用新的 Promise 替換掉最快改變狀態的 Promise

promises[res] = handler(sequence[currentIndex])。then(() => { return res });

})

}, Promise。resolve())。then(() => {

returnPromise。all(promises)

})

}

limitLoad(urls, loadImg, 3)

/*

因為 limitLoad 函式也返回一個 Promise,所以當 所有圖片載入完成後,可以繼續鏈式呼叫

limitLoad(urls, loadImg, 3)。then(() => {

console。log(‘所有圖片載入完成’);

})。catch(err => {

console。error(err);

})

*/

題目八

封裝一個非同步載入圖片的方法

function​loadImageAsync(url) {

returnnewPromise(function(resolve,reject) {

var image = new Image();

image。on​load = function() {

resolve(image)

};

image。on​error = function() {

reject(newError(‘Could not load image at’ + url));

};

image。src = url;

});

}

幾道經常會被問到的Promise面試題