Promise精選面試題

一饥努、前言

前端面試過程中,基本都會問到 Promise八回,如果你足夠幸運肪凛,面試官問的比較淺,僅僅問 Promise 的使用方式辽社,那么恭喜你伟墙。事實上,大多數人并沒有那么幸運滴铅。所以戳葵,我們要準備好九淺一深的知識。

??不知道讀者有沒有想過汉匙,為什么那么多面試官都喜歡問Promise拱烁?可以思考一下哦~

二、常見 Promise 面試題

我們看一些 Promise 的常見面試問法噩翠,由淺至深戏自。

  • 1、了解 Promise 嗎伤锚?
  • 2擅笔、Promise 解決的痛點是什么?
  • 3屯援、Promise 解決的痛點還有其他方法可以解決嗎猛们?如果有,請列舉狞洋。
  • 4弯淘、Promise 如何使用?
  • 5吉懊、Promise 常用的方法有哪些庐橙?它們的作用是什么假勿?
  • 6、Promise 在事件循環(huán)中的執(zhí)行過程是怎樣的态鳖?
  • 7转培、Promise 的業(yè)界實現都有哪些?
  • 8郁惜、能不能手寫一個 Promise 的 polyfill堡距。

這些問題,如果你都能 hold 住兆蕉,那么面試官基本認可你了羽戒。帶著上面這些問題,我們往下看虎韵。

Promise 出現的原因

??在 Promise 出現以前易稠,我們處理一個異步網絡請求离唬,大概是這樣:

// 請求 代表 一個異步網絡調用噪叙。
// 請求結果 代表網絡請求的響應隘世。
請求1(function(請求結果1){
    處理請求結果1
})

看起來還不錯吏砂。
但是,需求變化了彭雾,我們需要根據第一個網絡請求的結果疚顷,再去執(zhí)行第二個網絡請求壮池,代碼大概如下:

請求1(function(請求結果1){
    請求2(function(請求結果2){
        處理請求結果2
    })
})

看起來也不復雜硅瞧。
但是需求是永無止境的份乒,于是乎出現了如下的代碼:

請求1(function(請求結果1){
    請求2(function(請求結果2){
        請求3(function(請求結果3){
            請求4(function(請求結果4){
                請求5(function(請求結果5){
                    請求6(function(請求結果3){
                        ...
                    })
                })
            })
        })
    })
})

這回傻眼了。腕唧。或辖。 臭名昭著的 回調地獄 現身了。

更糟糕的是枣接,我們基本上還要對每次請求的結果進行一些處理颂暇,代碼會更加臃腫,在一個團隊中但惶,代碼 review 以及后續(xù)的維護將會是一個很痛苦的過程耳鸯。

回調地獄帶來的負面作用有以下幾點:

  • 代碼臃腫。
  • 可讀性差榆骚。
  • 耦合度過高片拍,可維護性差。
  • 代碼復用性差妓肢。
  • 容易滋生 bug。
  • 只能在回調里處理異常苫纤。

出現了問題碉钠,自然就會有人去想辦法纲缓。這時,就有人思考了喊废,能不能用一種更加友好的代碼組織方式祝高,解決異步嵌套的問題。

let 請求結果1 = 請求1();
let 請求結果2 = 請求2(請求結果1); 
let 請求結果3 = 請求3(請求結果2); 
let 請求結果4 = 請求2(請求結果3); 
let 請求結果5 = 請求3(請求結果4); 

類似上面這種同步的寫法污筷。 于是 Promise 規(guī)范誕生了工闺,并且在業(yè)界有了很多實現來解決回調地獄的痛點。比如業(yè)界著名的 Qbluebird瓣蛀,bluebird 甚至號稱運行最快的類庫陆蟆。

看官們看到這里,對于上面的問題 2 和問題 7 惋增,心中是否有了答案呢叠殷。

什么是 Promise

??Promise 是異步編程的一種解決方案,比傳統(tǒng)的異步解決方案【回調函數】和【事件】更合理诈皿、更強大×质現已被 ES6 納入進規(guī)范中。

代碼書寫比較

還是使用上面的網絡請求例子稽亏,我們看下 Promise 的常規(guī)寫法:

new Promise(請求1)
    .then(請求2(請求結果1))
    .then(請求3(請求結果2))
    .then(請求4(請求結果3))
    .then(請求5(請求結果4))
    .catch(處理異常(異常信息))

比較一下這種寫法和上面的回調式的寫法壶冒。我們不難發(fā)現,Promise 的寫法更為直觀截歉,并且能夠在外層捕獲異步函數的異常信息胖腾。

API

Promise 的常用 API 如下:

  • Promise.resolve(value)

類方法,該方法返回一個以 value 值解析后的 Promise 對象 1怎披、如果這個值是個 thenable(即帶有 then 方法)胸嘁,返回的 Promise 對象會“跟隨”這個 thenable 的對象,采用它的最終狀態(tài)(指 resolved/rejected/pending/settled)
2凉逛、如果傳入的 value 本身就是 Promise 對象性宏,則該對象作為 Promise.resolve 方法的返回值返回。
3状飞、其他情況以該值為成功狀態(tài)返回一個 Promise 對象毫胜。

上面是 resolve 方法的解釋,傳入不同類型的 value 值诬辈,返回結果也有區(qū)別酵使。這個 API 比較重要,建議大家通過練習一些小例子焙糟,并且配合上面的解釋來熟悉它口渔。如下幾個小例子:

//如果傳入的 value 本身就是 Promise 對象,則該對象作為 Promise.resolve 方法的返回值返回穿撮。  
function fn(resolve){
    setTimeout(function(){
        resolve(123);
    },3000);
}
let p0 = new Promise(fn);
let p1 = Promise.resolve(p0);
// 返回為true缺脉,返回的 Promise 即是 入參的 Promise 對象痪欲。
console.log(p0 === p1);

傳入 thenable 對象,返回 Promise 對象跟隨 thenable 對象的最終狀態(tài)攻礼。

ES6 Promises 里提到了 Thenable 這個概念业踢,簡單來說它就是一個非常類似 Promise 的東西。最簡單的例子就是 jQuery.ajax礁扮,它的返回值就是 thenable 對象知举。但是要謹記,并不是只要實現了 then 方法就一定能作為 Promise 對象來使用太伊。

//如果傳入的 value 本身就是 thenable 對象雇锡,返回的 promise 對象會跟隨 thenable 對象的狀態(tài)。
let promise = Promise.resolve($.ajax('/test/test.json'));// => promise對象
promise.then(function(value){
   console.log(value);
});

返回一個狀態(tài)已變成 resolved 的 Promise 對象倦畅。

let p1 = Promise.resolve(123); 
//打印p1 可以看到p1是一個狀態(tài)置為resolved的Promise對象
console.log(p1)

  • Promise.reject

類方法遮糖,且與 resolve 唯一的不同是,返回的 promise 對象的狀態(tài)為 rejected叠赐。

  • Promise.prototype.then

實例方法欲账,為 Promise 注冊回調函數,函數形式:fn(vlaue){}芭概,value 是上一個任務的返回結果赛不,then 中的函數一定要 return 一個結果或者一個新的 Promise 對象,才可以讓之后的then 回調接收罢洲。

  • Promise.prototype.catch

實例方法踢故,捕獲異常,函數形式:fn(err){}, err 是 catch 注冊 之前的回調拋出的異常信息惹苗。

  • Promise.race

類方法殿较,多個 Promise 任務同時執(zhí)行,返回最先執(zhí)行結束的 Promise 任務的結果桩蓉,不管這個 Promise 結果是成功還是失敗淋纲。 。

  • Promise.all

類方法院究,多個 Promise 任務同時執(zhí)行洽瞬。
如果全部成功執(zhí)行,則以數組的方式返回所有 Promise 任務的執(zhí)行結果业汰。 如果有一個 Promise 任務 rejected伙窃,則只返回 rejected 任務的結果。

  • ...
    以上幾種便是 Promise 的常用 API样漆,掌握了這些为障,我們便可以熟練使用 Promise了。

一定要多練習,熟練掌握产场,否則一知半解的理解在面試時捉襟見肘鹅髓。

如何理解 Promise

??為了便于理解 Promise舞竿,大家除了要多加練習以外京景,最好的方式是能夠將Promise的機制與現實生活中的例子聯系起來,這樣才能真正得到消化骗奖。
??我們可以把 Promise 比作一個保姆确徙,家里的一連串的事情,你只需要吩咐給他执桌,他就能幫你做鄙皇,你就可以去做其他事情了。
??比如仰挣,某一天要出門辦事伴逸,但是我還要買菜做飯送到老婆單位。
??出門辦的事情很重要膘壶,買菜做飯也重要错蝴。但我自己只能做一件事。
這時我就可以把買菜做飯的事情交給保姆颓芭,我會告訴她:

  • 你先去超市買菜顷锰。
  • 用超市買回來的菜做飯。
  • 將做好的飯菜送到老婆單位亡问。
  • 送到單位后打電話告訴我官紫。

我們知道,上面三步都是需要消耗時間的州藕,我們可以理解為三個異步任務束世。利用 Promise 的寫法來書寫這個操作:

function 買菜(resolve,reject) {
    setTimeout(function(){
        resolve(['西紅柿'床玻、'雞蛋'毁涉、'油菜']);
    },3000)
}
function 做飯(resolve, reject){
    setTimeout(function(){
        //對做好的飯進行下一步處理。
        resolve ({
            主食: '米飯',
            菜: ['西紅柿炒雞蛋'笨枯、'清炒油菜']
        })
    },3000) 
}
function 送飯(resolve薪丁,reject){
    //對送飯的結果進行下一步處理
    resolve('老婆的么么噠');
}
function 電話通知我(){
    //電話通知我后的下一步處理
    給保姆加100塊錢獎金;
}

好了,現在我整理好了四個任務馅精,這時我需要告訴保姆严嗜,讓他按照這個任務列表去做。這個過程是必不可少的洲敢,因為如果不告訴保姆漫玄,保姆不知道需要做這些事情。

// 告訴保姆幫我做幾件連貫的事情,先去超市買菜
new Promise(買菜)
//用買好的菜做飯
.then((買好的菜)=>{
    return new Promise(做飯);
})
//把做好的飯送到老婆公司
.then((做好的飯)=>{
    return new Promise(送飯);
})
//送完飯后打電話通知我
.then((送飯結果)=>{
    電話通知我();
})

至此睦优,我通知了保姆要做這些事情渗常,然后我就可以放心地去辦我的事情。

請一定要謹記:如果我們的后續(xù)任務是異步任務的話汗盘,必須return 一個 新的 promise 對象皱碘。
如果后續(xù)任務是同步任務,只需 return 一個結果即可隐孽。
我們上面舉的例子癌椿,除了電話通知我是一個同步任務,其余的都是異步任務菱阵,異步任務 return 的是 promise對象踢俄。

除此之外,一定謹記晴及,一個 Promise 對象有三個狀態(tài)都办,并且狀態(tài)一旦改變,便不能再被更改為其他狀態(tài)虑稼。

  • pending琳钉,異步任務正在進行。
  • resolved (也可以叫fulfilled)动雹,異步任務執(zhí)行成功槽卫。
  • rejected,異步任務執(zhí)行失敗胰蝠。

Promise的使用總結歼培。

??Promise 這么多概念,初學者很難一下子消化掉茸塞,那么我們可以采取強制記憶法躲庄,強迫自己去記住使用過程。

  • 首先初始化一個 Promise 對象钾虐,可以通過兩種方式創(chuàng)建噪窘, 這兩種方式都會返回一個 Promise 對象。

    • 1效扫、new Promise(fn)
    • 2倔监、Promise.resolve(fn)
  • 然后調用上一步返回的 promise 對象的 then 方法,注冊回調函數菌仁。

  • then 中的回調函數可以有一個參數浩习,也可以不帶參數。如果 then 中的回調函數依賴上一步的返回結果济丘,那么要帶上參數谱秽。比如:

        new Promise(fn)
        .then(fn1(value){
            //處理value
        })
    
  • 最后注冊 catch 異常處理函數洽蛀,處理前面回調中可能拋出的異常。

??通常按照這三個步驟疟赊,你就能夠應對絕大部分的異步處理場景郊供。用熟之后,再去研究 Promise 各個函數更深層次的原理以及使用方式即可近哟。

??看到這里之后驮审,我們便能回答上面的問題 4 和問題 5了。

Promsie 與事件循環(huán)

??Promise在初始化時椅挣,傳入的函數是同步執(zhí)行的头岔,然后注冊 then 回調。注冊完之后鼠证,繼續(xù)往下執(zhí)行同步代碼,在這之前靠抑,then 中回調不會執(zhí)行量九。同步代碼塊執(zhí)行完畢后,才會在事件循環(huán)中檢測是否有可用的 promise 回調颂碧,如果有荠列,那么執(zhí)行,如果沒有载城,繼續(xù)下一個事件循環(huán)肌似。

??關于 Promise 在事件循環(huán)中還有一個 微任務的概念(microtask),感興趣的話可以看另外一篇關于nodejs 時間循環(huán)的文章 剖析nodejs的事件循環(huán)诉瓦,雖然和瀏覽器端有些不同川队,但是Promise 微任務的執(zhí)行時機相差不大。

Promise 的升級

??ES6 出現了 generator 以及 async/await 語法睬澡,使異步處理更加接近同步代碼寫法固额,可讀性更好,同時異常捕獲和同步代碼的書寫趨于一致煞聪。上面的列子可以寫成這樣:

(async ()=>{
    let 蔬菜 = await 買菜();
    let 飯菜 = await 做飯(蔬菜);
    let 送飯結果 = await 送飯(飯菜);
    let 通知結果 = await 通知我(送飯結果);
})();

是不是更清晰了有沒有斗躏。需要記住的是,async/await也是基于 Promise 實現的昔脯,所以啄糙,我們仍然有必要深入理解 Promise 的用法。

結語

上面的內容只是精選面試題里的知識云稚,若是吃透 Promise 的使用與原理隧饼,就要多加練習了,這樣會讓面試更加從容碱鳞。

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末桑李,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌贵白,老刑警劉巖率拒,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異禁荒,居然都是意外死亡猬膨,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門呛伴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來勃痴,“玉大人,你說我怎么就攤上這事热康∨嫔辏” “怎么了?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵姐军,是天一觀的道長铁材。 經常有香客問我,道長奕锌,這世上最難降的妖魔是什么著觉? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮惊暴,結果婚禮上饼丘,老公的妹妹穿的比我還像新娘。我一直安慰自己辽话,他們只是感情好肄鸽,可當我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著屡穗,像睡著了一般贴捡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上村砂,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天烂斋,我揣著相機與錄音,去河邊找鬼础废。 笑死汛骂,一個胖子當著我的面吹牛,可吹牛的內容都是我干的评腺。 我是一名探鬼主播帘瞭,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蒿讥!你這毒婦竟也來了蝶念?” 一聲冷哼從身側響起抛腕,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎媒殉,沒想到半個月后担敌,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡廷蓉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年全封,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片桃犬。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡刹悴,死狀恐怖,靈堂內的尸體忽然破棺而出攒暇,到底是詐尸還是另有隱情土匀,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布扯饶,位于F島的核電站恒削,受9級特大地震影響尾序,放射性物質發(fā)生泄漏躯砰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一琢歇、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧李茫,春花似錦揭保、人聲如沸魄宏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至予跌,卻和暖如春搏色,著一層夾襖步出監(jiān)牢的瞬間券册,已是汗流浹背垂涯。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工航邢, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留耕赘,地道東北人。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓翠忠,卻偏偏與公主長得像鞠苟,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子秽之,可洞房花燭夜當晚...
    茶點故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內容

  • 官方中文版原文鏈接 感謝社區(qū)中各位的大力支持当娱,譯者再次奉上一點點福利:阿里云產品券,享受所有官網優(yōu)惠考榨,并抽取幸運大...
    HetfieldJoe閱讀 11,026評論 26 95
  • Promise 對象 Promise 的含義 Promise 是異步編程的一種解決方案跨细,比傳統(tǒng)的解決方案——回調函...
    neromous閱讀 8,705評論 1 56
  • 你不知道JS:異步 第三章:Promises 在第二章,我們指出了采用回調來表達異步和管理并發(fā)時的兩種主要不足:缺...
    purple_force閱讀 2,066評論 0 4
  • 特別說明河质,為便于查閱冀惭,文章轉自https://github.com/getify/You-Dont-Know-JS...
    殺破狼real閱讀 885評論 0 2
  • 春雪 文/胡玉芝 思念似潮水一般 時不時的涌現 春與雪的呢喃 竟是如此的纏綿 冬偷偷的往返 無法釋懷對春姑娘的迷戀...
    詩意的棲居_b130閱讀 203評論 0 1