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