面试题目_总结面试中 promise 相关题目的套路
Promise 作為當(dāng)下主流的異步解決方案,在工作中和面試中常常出現(xiàn),尤其是在面試中,會(huì)弄個(gè)場景讓你手寫代碼,這里給大家介紹五道比較有代表性的題目,以便熟悉一些套路。
promise 簡單介紹
先簡單介紹下 Promise
Promise 對(duì)象用于表示一個(gè)異步操作的最終完成 (或失敗), 及其結(jié)果值。可以為異步操作的成功和失敗綁定執(zhí)行函數(shù),讓異步方法可以像同步方法一樣返回值,但立即返回的是一個(gè)能代表未來可能出現(xiàn)結(jié)果的Promise對(duì)象。
Promise 對(duì)象有三種狀態(tài):
- pending: 初始狀態(tài),既不是成功,也不是失敗狀態(tài)。
- fulfilled: 意味著操作成功完成。
- rejected: 意味著操作失敗。
Promise 的使用和提供的靜態(tài)方法:
- new Promise( function(resolve, reject) {...} /* executor */ ); :返回 Promise 對(duì)象
- Promise.all(iterable) :iterable參數(shù)對(duì)象里所有的promise對(duì)象都成功的時(shí)候才會(huì)觸發(fā)成功,若一個(gè)失敗,則立即觸發(fā)返回Promise對(duì)象的失敗
- Promise.race(iterable):iterable參數(shù)中的一個(gè)成功或者失敗都會(huì)立即觸發(fā)返回對(duì)象的成功和失敗
- Promise.reject(reason):返回一個(gè)狀態(tài)為失敗的Promise對(duì)象
- Promise.resolve(value):返回一個(gè)狀態(tài)由value給定的Promise對(duì)象,通常用于將一個(gè)值以Promise的方式使用。
下面開始看題
題一
與js事件循環(huán)結(jié)合出題,如下,寫出執(zhí)行結(jié)果
console.log('script?start')async?function?async1()?{
????await?async2()
????console.log('async1?end')
}
async?function?async2()?{console.log('async2?end')}
async1()
setTimeout(function?()?{console.log('setTimeout')},?0)
new?Promise(resolve?=>?{
????console.log('Promise')
????resolve()
}).then(function?()?{
????????console.log('promise1')
????}).then(function?()?{
????????console.log('promise2')
????})
console.log('script?end')
//?結(jié)果如下
//?script?start
//?async2?end
//?Promise
//?script?end
//?async1?end
//?promise1
//?promise2
//?setTimeout
掌握事件循環(huán)機(jī)制和明白 Promise.then() 屬于微隊(duì)列,這一類的題目就都是一個(gè)套路。
題二
實(shí)現(xiàn)如下調(diào)用,lazyMan('xxx').sleep(1000).eat('333').sleepFirst(2000) sleepFirst 最先執(zhí)行。
這題考察如何組合多個(gè) Promise 和鏈?zhǔn)秸{(diào)用。
可以用數(shù)組將 sleep eat 等函數(shù)暫存,同時(shí)為了能鏈?zhǔn)秸{(diào)用,所以每個(gè)函數(shù)需返回 Promise 對(duì)象。那么什么時(shí)候執(zhí)行數(shù)組中的函數(shù)呢?
根據(jù)事件循環(huán)機(jī)制,我們用 setTimeout 來執(zhí)行數(shù)組中的方法,在定時(shí)器的回調(diào)函數(shù)中相關(guān)的事件已經(jīng)添加到數(shù)組中了,鏈?zhǔn)綀?zhí)行數(shù)組中方法前,需要有一個(gè)構(gòu)建一個(gè) Promise 對(duì)象來執(zhí)行 then 方法,可以通過 Promise.resolve() 返回一個(gè) Promise 對(duì)象。
function?lazyMan(name)?{????this.task?=?[];
????this.task.push(()?=>?{
????????return?new?Promise(res?=>?{
????????????console.log('name:'?+?name);res()
????????})
????})
????let?run?=?()?=>?{
????????let?sequence?=?Promise.resolve()
????????for?(const?func?of?this.task)?{
????????????sequence?=?sequence.then(()=>func())
????????}
????}
????setTimeout(()?=>?{run()},?0)
????this.eat?=?(str)?=>?{
????????this.task.push(()?=>?{
????????????return?new?(res?=>?{
????????????????console.log('eat:'?+?str);res()
????????????})
????????})
????????return?this;
????}
????this.sleep?=?(time)?=>?{
????????this.task.push(()?=>?{
????????????return?new?Promise(res?=>?{
????????????????setTimeout(()?=>?{
????????????????????console.log(`Wake?up?after?`?+?time);res()
????????????????},?time)
????????????})
????????})
????????return?this;
????}
????this.sleepFirst?=?(time)?=>?{
????????this.task.unshift(()?=>?{
????????????return?new?Promise(res?=>?{
????????????????setTimeout(()?=>?{
????????????????????console.log(`sleepFirst?up?after?`?+?time);res()
????????????????},?time)
????????????})
????????})
????????return?this;
????}
????return?this;
}
題三
任務(wù)隊(duì)列可不斷的添加異步任務(wù)(異步任務(wù)都是Promise),但只能同時(shí)處理5個(gè)任務(wù),5個(gè)一組執(zhí)行完成后才能執(zhí)行下一組,任務(wù)隊(duì)列為空時(shí)暫停執(zhí)行,當(dāng)有新任務(wù)加入則自動(dòng)執(zhí)行。
class?RunQune{????constructor(){
????????this.list?=?[];?//?任務(wù)隊(duì)列
????????this.target?=?5;?//?并發(fā)數(shù)量
????????this.flag?=?false;?//?任務(wù)執(zhí)行狀態(tài)
????????this.time?=?Date.now()
????}
????async?sleep(time){
????????return?new?Promise(res=>setTimeout(res,time))
????}
????//?執(zhí)行任務(wù)
????async?run(){
????????while(this.list.length>0){
????????????this.flag?=?true;
????????????let?runList?=?this.list.splice(0,this.target);
????????????this.time?=?Date.now()
????????????await?this.runItem(runList)
????????????await?this.sleep(300)?//?模擬執(zhí)行時(shí)間
????????}
????????this.flag?=?false;
????}
????async?runItem(list){
????????return?new?Promise((res)=>{
????????????while(list.length>0){
????????????????const?fn?=?list.shift();
????????????????fn().then().finally(()=>{
????????????????????if(list.length?===?0){
????????????????????????res()
????????????????????}
????????????????})
????????????}
????????})
????}
????//?添加任務(wù)
????push(task){
????????this.list.push(...task);
????????!this.flag?&&?this.run()
????}
}
這題還可以進(jìn)一步發(fā)散,不需要等待一組完成在執(zhí)行下一組,只要并發(fā)量沒有滿,就可以加入新的任務(wù)執(zhí)行,實(shí)現(xiàn)的思路沒太大變化,在 finally 中改為新增任務(wù)。
題四
期望id按順序打印 0 1 2 3 4 ,且只能修改 start 函數(shù)。
function?start(id)?{????execute(id)
}
for?(let?i?=?0;?i?5;?i++)?{
????start(i);
}
function?sleep()?{
????const?duration?=?Math.floor(Math.random()?*?500);
????return?new?Promise(resolve?=>?setTimeout(resolve,?duration));
}
function?execute(id)?{
????return?sleep().then(()?=>?{
????????console.log("id",?id);
????});
}
id 的打印是個(gè)異步事件,在 setTimeout 回調(diào)執(zhí)行,按照上面的代碼,誰的倒計(jì)時(shí)先結(jié)束,id就先打印,那么想要id按順序打印,就需要將多個(gè)異步事件同步執(zhí)行,promise 的鏈?zhǔn)秸{(diào)用可以派上用場。代碼如下
function?start(id)?{????//?execute(id)
????//?第一種:promise 鏈?zhǔn)秸{(diào)用,execute 函數(shù)返回的就是 promise ,所以可以利用這一點(diǎn),通過 promise.then 依次執(zhí)行下一個(gè)打印
????this.promise?=?this.promise???this.promise.then(()=>execute(id))?:?execute(id)
????//?第二種:先用數(shù)組存儲(chǔ)異步函數(shù),利用事件循環(huán)的下一個(gè)階段,即 setTimeout 的回調(diào)函數(shù)中執(zhí)行 promise 的鏈?zhǔn)秸{(diào)用,這方法本質(zhì)上和第一種是一樣的
????this.list?=?this.list???this.list?:?[]
????this.list.push(()?=>?execute(id))
????this.t;
????if?(this.t)?clearTimeout(this.t)
????this.t?=?setTimeout(()?=>?{
????????this.list.reduce((re,?fn)?=>?re.then(()?=>?fn()),?Promise.resolve())
????})
????//?第三種:數(shù)組存儲(chǔ)id的值,在通過 await 異步執(zhí)行 execute 函數(shù)
????this.list?=?this.list???this.list?:?[]
????this.list.push(id)
????clearTimeout(this.t)
????this.t?=?setTimeout(async?()?=>?{
????????let?_id?=?this.list.shift()
????????while?(_id?!==?undefined)?{
????????????await?execute(_id);
????????????_id?=?this.list.shift()
????????}
????})
}
題五
手撕源碼系列,來手寫一個(gè)Promise,在動(dòng)手前需要先了解 Promise/A+ 規(guī)范,列舉關(guān)鍵部分的規(guī)范,詳細(xì)規(guī)范可見文末鏈接
根據(jù)這三點(diǎn)我實(shí)現(xiàn)了一個(gè)簡化版的 Promise
function?MPromise(executor)?{????this.status?=?'pending';?//?pending?,?fulfilled?,?rejected?
????this.data?=?''?//?當(dāng)前promise的值,主要用于?then?方法中的?fulfilled?,?rejected?兩種狀態(tài)的處理
????this.resolveFuncList?=?[];?//??使用數(shù)組的原因是,一個(gè)promise可以同時(shí)執(zhí)行多個(gè)?then?方法,?也就會(huì)同時(shí)存在多個(gè)then回調(diào)
????this.rejectFunc;
????const?self?=?this;
????function?resolve(value)?{
????????//?使用?setTimeout?實(shí)現(xiàn)異步
????????setTimeout(()?=>?{
????????????if?(self.status?===?'pending')?{
????????????????self.status?=?'fulfilled';
????????????????self.data?=?value;
????????????????//?執(zhí)行?resolve?函數(shù)
????????????????self.resolveFuncList.forEach(func?=>?{
????????????????????func(value)
????????????????});
????????????}
????????})
????}
????function?reject(reason)?{
????????setTimeout(()?=>?{
????????????if?(self.status?===?'pending')?{
????????????????self.status?=?'rejected';
????????????????self.data?=?value;
????????????????self.rejectFunc?&&?self.rejectFunc(reason);
????????????}
????????})
????}
????try?{
????????executor(resolve,?reject)
????}?catch?(error)?{
????????reject(error)
????}
}
MPromise.prototype.then?=?function?(onFulfilled,?onRejected)?{
????let?promise2;
????//?區(qū)分不同狀態(tài)下的處理
????if?(this.status?===?'pending')?{
????????return?promise2?=?new?MPromise((res,?rej)?=>?{
????????????this.resolveFuncList.push(function?(value)?{
????????????????let?x?=?onFulfilled(value);
????????????????resolvePromise(promise2,?x,?res,?rej)
????????????})
????????????this.rejectFunc?=?function?(reason)?{
????????????????let?x?=?onRejected(reason);
????????????????resolvePromise(promise2,?x,?res,?rej)
????????????}
????????})
????}
????if?(this.status?===?'fulfilled')?{
????????return?promise2?=?new?MPromise((res,?rej)?=>?{
????????????setTimeout(()?=>?{
????????????????let?x?=?onFulfilled(this.data)?//?輸出將上一次執(zhí)行結(jié)果
????????????????resolvePromise(promise2,?x,?res,?rej)
????????????})
????????})
????}
????if?(this.status?===?'rejected')?{
????????return?promise2?=?new?MPromise((res,?rej)?=>?{
????????????setTimeout(()?=>?{
????????????????let?x?=?onRejected(this.data)
????????????????resolvePromise(promise2,?x,?res,?rej)
????????????})
????????})
????}
}
function?resolvePromise(promise2,?x,?resolve,?reject)?{
????if?(x?instanceof?MPromise)?{
????????if?(x.status?===?'pending')?{
????????????x.then(value?=>?{
????????????????resolvePromise(promise2,?value,?resolve,?reject)
????????????},?reason?=>?{
????????????????reject(reason)
????????????})
????????}?else?{
????????????x.then(resolve,?reject)
????????}
????}?else?{
????????resolve(x)
????}
}
有的因?yàn)闀r(shí)間有限,會(huì)讓手寫 Promise 的 api,以下兩個(gè)就常常被問到
1. 手寫一個(gè)?Promise.all
/**?*?Promise.all?Promise進(jìn)行并行處理
?*?參數(shù):?promise對(duì)象組成的數(shù)組作為參數(shù)
?*?返回值:?返回一個(gè)Promise實(shí)例
?*?當(dāng)這個(gè)數(shù)組里的所有promise對(duì)象全部進(jìn)入FulFilled狀態(tài)的時(shí)候,才會(huì)resolve。
?*/
Promise.all?=?function(promises)?{
????return?new?Promise((resolve,?reject)?=>?{
????????let?values?=?[]
????????let?count?=?0
????????promises.forEach((promise,?index)?=>?{
????????????promise.then(value?=>?{
????????????????console.log('value:',?value,?'index:',?index)
????????????????values[index]?=?value
????????????????count++
????????????????if?(count?===?promises.length)?{
????????????????????resolve(values)
????????????????}
????????????},?reject)
????????})
????})
}
2. 手寫一個(gè) Promise.rase
/**?*?Promise.race
?*?參數(shù):?接收?promise對(duì)象組成的數(shù)組作為參數(shù)
?*?返回值:?返回一個(gè)Promise實(shí)例
?*?只要有一個(gè)promise對(duì)象進(jìn)入?FulFilled?或者?Rejected?狀態(tài)的話,就會(huì)繼續(xù)進(jìn)行后面的處理(取決于哪一個(gè)更快)
?*/
Promise.race?=?function(promises)?{
????return?new?Promise((resolve,?reject)?=>?{
????????promises.forEach((promise)?=>?{
????????????promise.then(resolve,?reject);
????????});
????});
}
小結(jié)
文中代碼略長,在電腦上查看效果更佳。
若能幫到大伙,期望能給個(gè)點(diǎn)贊鼓勵(lì)~
歡迎大伙在公號(hào)對(duì)話框點(diǎn)擊交個(gè)盆友,添加我微信一起探討交流~
參考文章
https://www.ituring.com.cn/article/66566?
https://promisesaplus.com/
總結(jié)
以上是生活随笔為你收集整理的面试题目_总结面试中 promise 相关题目的套路的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 桂花的优美句子158个
- 下一篇: 不等号属于不等式吗_考研专业课备考时,仅