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’];
functionloadImg(url) {
returnnewPromise((resolve, reject) => {
const img = new Image()
img。onload = function () {
console。log(‘一張圖片載入完成’);
resolve();
}
img。onerror = 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’];
functionloadImg(url) {
returnnewPromise((resolve, reject) => {
const img = new Image()
img。onload = function () {
console。log(‘一張圖片載入完成’);
resolve();
}
img。onerror = 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);
})
*/
題目八
封裝一個非同步載入圖片的方法
functionloadImageAsync(url) {
returnnewPromise(function(resolve,reject) {
var image = new Image();
image。onload = function() {
resolve(image)
};
image。onerror = function() {
reject(newError(‘Could not load image at’ + url));
};
image。src = url;
});
}