Promise 必備知識(shí)匯總和面試情況
微信搜索逆鋒起筆關(guān)注后回復(fù)編程pdf
領(lǐng)取編程大佬們所推薦的 23 種編程資料!
來源:https://segmentfault.com/a/1190000039699000
作者:小磊
寫在前面
Javascript異步編程先后經(jīng)歷了四個(gè)階段,分別是Callback階段,Promise階段,Generator階段和Async/Await階段。Callback很快就被發(fā)現(xiàn)存在回調(diào)地獄和控制權(quán)問題,Promise就是在這個(gè)時(shí)間出現(xiàn),用以解決這些問題,Promise并非一個(gè)新事務(wù),而是按照一個(gè)規(guī)范實(shí)現(xiàn)的類,這個(gè)規(guī)范有很多,如 Promise/A,Promise/B,Promise/D以及 Promise/A 的升級(jí)版 Promise/A+,最終 ES6 中采用了 Promise/A+ 規(guī)范。后來出現(xiàn)的Generator函數(shù)以及Async函數(shù)也是以Promise為基礎(chǔ)的進(jìn)一步封裝,可見Promise在異步編程中的重要性。
關(guān)于Promise的資料已經(jīng)很多,但每個(gè)人理解都不一樣,不同的思路也會(huì)有不一樣的收獲。這篇文章會(huì)著重寫一下Promise的實(shí)現(xiàn)以及筆者在日常使用過程中的一些心得體會(huì)。
實(shí)現(xiàn)Promise
規(guī)范解讀
Promise/A+規(guī)范主要分為術(shù)語(yǔ)、要求和注意事項(xiàng)三個(gè)部分,我們重點(diǎn)看一下第二部分也就是要求部分,以筆者的理解大概說明一下,具體細(xì)節(jié)參照完整版Promise/A+標(biāo)準(zhǔn)。
1、
Promise有三種狀態(tài)pending,fulfilled和rejected。(為了一致性,此文章稱fulfilled狀態(tài)為resolved狀態(tài))
狀態(tài)轉(zhuǎn)換只能是 pending到resolved或者pending到rejected;狀態(tài)一旦轉(zhuǎn)換完成,不能再次轉(zhuǎn)換。 2、
Promise擁有一個(gè)then方法,用以處理resolved或rejected狀態(tài)下的值。
then方法接收兩個(gè)參數(shù)onFulfilled和onRejected,這兩個(gè)參數(shù)變量類型是函數(shù),如果不是函數(shù)將會(huì)被忽略,并且這兩個(gè)參數(shù)都是可選的。then方法必須返回一個(gè)新的promise,記作promise2,這也就保證了then方法可以在同一個(gè)promise上多次調(diào)用。(ps:規(guī)范只要求返回promise,并沒有明確要求返回一個(gè)新的promise,這里為了跟ES6實(shí)現(xiàn)保持一致,我們也返回一個(gè)新promise)onResolved/onRejected有返回值則把返回值定義為x,并執(zhí)行[[Resolve]](promise2, x);onResolved/onRejected運(yùn)行出錯(cuò),則把promise2設(shè)置為rejected狀態(tài);onResolved/onRejected不是函數(shù),則需要把promise1的狀態(tài)傳遞下去。3、不同的
promise實(shí)現(xiàn)可以的交互。
規(guī)范中稱這一步操作為 promise解決過程,函數(shù)標(biāo)示為[[Resolve]](promise, x),promise為要返回的新promise對(duì)象,x為onResolved/onRejected的返回值。如果x有then方法且看上去像一個(gè)promise,我們就把x當(dāng)成一個(gè)promise的對(duì)象,即thenable對(duì)象,這種情況下嘗試讓promise接收x的狀態(tài)。如果x不是thenable對(duì)象,就用x的值來執(zhí)行promise。[[Resolve]](promise, x)函數(shù)具體運(yùn)行規(guī)則:
如果 promise和x指向同一對(duì)象,以TypeError為據(jù)因拒絕執(zhí)行promise;如果 x為Promise,則使promise接受x的狀態(tài);如果 x為對(duì)象或者函數(shù),取x.then的值,如果取值時(shí)出現(xiàn)錯(cuò)誤,則讓promise進(jìn)入rejected狀態(tài),如果then不是函數(shù),說明x不是thenable對(duì)象,直接以x的值resolve,如果then存在并且為函數(shù),則把x作為then函數(shù)的作用域this調(diào)用,then方法接收兩個(gè)參數(shù),resolvePromise和rejectPromise,如果resolvePromise被執(zhí)行,則以resolvePromise的參數(shù)value作為x繼續(xù)調(diào)用[[Resolve]](promise, value),直到x不是對(duì)象或者函數(shù),如果rejectPromise被執(zhí)行則讓promise進(jìn)入rejected狀態(tài);如果 x不是對(duì)象或者函數(shù),直接就用x的值來執(zhí)行promise。
代碼實(shí)現(xiàn)
規(guī)范解讀第1條,代碼實(shí)現(xiàn):
class Promise {
// 定義Promise狀態(tài),初始值為pending
status = 'pending';
// 狀態(tài)轉(zhuǎn)換時(shí)攜帶的值,因?yàn)樵?span style="color: rgb(198, 120, 221);line-height: 26px;">then方法中需要處理Promise成功或失敗時(shí)的值,所以需要一個(gè)全局變量存儲(chǔ)這個(gè)值
data = '';
// Promise構(gòu)造函數(shù),傳入?yún)?shù)為一個(gè)可執(zhí)行的函數(shù)
constructor(executor) {
// resolve函數(shù)負(fù)責(zé)把狀態(tài)轉(zhuǎn)換為resolved
function resolve(value) {
this.status = 'resolved';
this.data = value;
}
// reject函數(shù)負(fù)責(zé)把狀態(tài)轉(zhuǎn)換為rejected
function reject(reason) {
this.status = 'rejected';
this.data = reason;
}
// 直接執(zhí)行executor函數(shù),參數(shù)為處理函數(shù)resolve, reject。因?yàn)閑xecutor執(zhí)行過程有可能會(huì)出錯(cuò),錯(cuò)誤情況需要執(zhí)行reject
try {
executor(resolve, reject);
} catch(e) {
reject(e)
}
}
}
第1條就是實(shí)現(xiàn)完畢了,相對(duì)簡(jiǎn)單,配合代碼注釋很容易理解。
規(guī)范解讀第2條,代碼實(shí)現(xiàn):
/**
* 擁有一個(gè)then方法
* then方法提供:狀態(tài)為resolved時(shí)的回調(diào)函數(shù)onResolved,狀態(tài)為rejected時(shí)的回調(diào)函數(shù)onRejected
* 返回一個(gè)新的Promise
*/
then(onResolved, onRejected) {
// 設(shè)置then的默認(rèn)參數(shù),默認(rèn)參數(shù)實(shí)現(xiàn)Promise的值的穿透
onResolved = typeof onResolved === 'function' ? onResolved : function(v) { return e };
onRejected = typeof onRejected === 'function' ? onRejected : function(e) { throw e };
let promise2;
promise2 = new Promise((resolve, reject) => {
// 如果狀態(tài)為resolved,則執(zhí)行onResolved
if (this.status === 'resolved') {
try {
// onResolved/onRejected有返回值則把返回值定義為x
const x = onResolved(this.data);
// 執(zhí)行[[Resolve]](promise2, x)
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}
// 如果狀態(tài)為rejected,則執(zhí)行onRejected
if (this.status === 'rejected') {
try {
const x = onRejected(this.data);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}
});
return promise2;
}
現(xiàn)在我們就按照規(guī)范解讀第2條,實(shí)現(xiàn)了上述代碼,上述代碼很明顯是有問題的,問題如下
-
resolvePromise未定義; -
then方法執(zhí)行的時(shí)候,promise可能仍然處于pending狀態(tài),因?yàn)?code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: 14px;border-radius: 4px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.047);font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace;word-break: break-all;">executor中可能存在異步操作(實(shí)際情況大部分為異步操作),這樣就導(dǎo)致onResolved/onRejected失去了執(zhí)行時(shí)機(jī); -
onResolved/onRejected這兩個(gè)函數(shù)需要異步調(diào)用(官方Promise實(shí)現(xiàn)的回調(diào)函數(shù)總是異步調(diào)用的)。
解決辦法:
-
根據(jù)規(guī)范解讀第3條,定義并實(shí)現(xiàn) resolvePromise函數(shù); -
then方法執(zhí)行時(shí)如果promise仍然處于pending狀態(tài),則把處理函數(shù)進(jìn)行儲(chǔ)存,等resolve/reject函數(shù)真正執(zhí)行的的時(shí)候再調(diào)用。 -
promise.then屬于微任務(wù),這里我們?yōu)榱朔奖?,用宏任?wù)setTiemout來代替實(shí)現(xiàn)異步,具體細(xì)節(jié)特別推薦這篇文章。
好了,有了解決辦法,我們就把代碼進(jìn)一步完善:
class Promise {
// 定義Promise狀態(tài)變量,初始值為pending
status = 'pending';
// 因?yàn)樵?span style="color: rgb(198, 120, 221);line-height: 26px;">then方法中需要處理Promise成功或失敗時(shí)的值,所以需要一個(gè)全局變量存儲(chǔ)這個(gè)值
data = '';
// Promise resolve時(shí)的回調(diào)函數(shù)集
onResolvedCallback = [];
// Promise reject時(shí)的回調(diào)函數(shù)集
onRejectedCallback = [];
// Promise構(gòu)造函數(shù),傳入?yún)?shù)為一個(gè)可執(zhí)行的函數(shù)
constructor(executor) {
// resolve函數(shù)負(fù)責(zé)把狀態(tài)轉(zhuǎn)換為resolved
function resolve(value) {
this.status = 'resolved';
this.data = value;
for (const func of this.onResolvedCallback) {
func(this.data);
}
}
// reject函數(shù)負(fù)責(zé)把狀態(tài)轉(zhuǎn)換為rejected
function reject(reason) {
this.status = 'rejected';
this.data = reason;
for (const func of this.onRejectedCallback) {
func(this.data);
}
}
// 直接執(zhí)行executor函數(shù),參數(shù)為處理函數(shù)resolve, reject。因?yàn)閑xecutor執(zhí)行過程有可能會(huì)出錯(cuò),錯(cuò)誤情況需要執(zhí)行reject
try {
executor(resolve, reject);
} catch(e) {
reject(e)
}
}
/**
* 擁有一個(gè)then方法
* then方法提供:狀態(tài)為resolved時(shí)的回調(diào)函數(shù)onResolved,狀態(tài)為rejected時(shí)的回調(diào)函數(shù)onRejected
* 返回一個(gè)新的Promise
*/
then(onResolved, onRejected) {
// 設(shè)置then的默認(rèn)參數(shù),默認(rèn)參數(shù)實(shí)現(xiàn)Promise的值的穿透
onResolved = typeof onResolved === 'function' ? onResolved : function(v) { return e };
onRejected = typeof onRejected === 'function' ? onRejected : function(e) { throw e };
let promise2;
promise2 = new Promise((resolve, reject) => {
// 如果狀態(tài)為resolved,則執(zhí)行onResolved
if (this.status === 'resolved') {
setTimeout(() => {
try {
// onResolved/onRejected有返回值則把返回值定義為x
const x = onResolved(this.data);
// 執(zhí)行[[Resolve]](promise2, x)
this.resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
// 如果狀態(tài)為rejected,則執(zhí)行onRejected
if (this.status === 'rejected') {
setTimeout(() => {
try {
const x = onRejected(this.data);
this.resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
// 如果狀態(tài)為pending,則把處理函數(shù)進(jìn)行存儲(chǔ)
if (this.status = 'pending') {
this.onResolvedCallback.push(() => {
setTimeout(() => {
try {
const x = onResolved(this.data);
this.resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
this.onRejectedCallback.push(() => {
setTimeout(() => {
try {
const x = onRejected(this.data);
this.resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
}
});
return promise2;
}
// [[Resolve]](promise2, x)函數(shù)
resolvePromise(promise2, x, resolve, reject) {
}
}
至此,規(guī)范中關(guān)于then的部分就全部實(shí)現(xiàn)完畢了。代碼添加了詳細(xì)的注釋,參考注釋不難理解。
規(guī)范解讀第3條,代碼實(shí)現(xiàn):
// [[Resolve]](promise2, x)函數(shù)
resolvePromise(promise2, x, resolve, reject) {
let called = false;
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise!'))
}
// 如果x仍然為Promise的情況
if (x instanceof Promise) {
// 如果x的狀態(tài)還沒有確定,那么它是有可能被一個(gè)thenable決定最終狀態(tài)和值,所以需要繼續(xù)調(diào)用resolvePromise
if (x.status === 'pending') {
x.then(function(value) {
resolvePromise(promise2, value, resolve, reject)
}, reject)
} else {
// 如果x狀態(tài)已經(jīng)確定了,直接取它的狀態(tài)
x.then(resolve, reject)
}
return
}
if (x !== null && (Object.prototype.toString(x) === '[object Object]' || Object.prototype.toString(x) === '[object Function]')) {
try {
// 因?yàn)閤.then有可能是一個(gè)getter,這種情況下多次讀取就有可能產(chǎn)生副作用,所以通過變量called進(jìn)行控制
const then = x.then
// then是函數(shù),那就說明x是thenable,繼續(xù)執(zhí)行resolvePromise函數(shù),直到x為普通值
if (typeof then === 'function') {
then.call(x, (y) => {
if (called) return;
called = true;
this.resolvePromise(promise2, y, resolve, reject);
}, (r) => {
if (called) return;
called = true;
reject(r);
})
} else { // 如果then不是函數(shù),那就說明x不是thenable,直接resolve x
if (called) return ;
called = true;
resolve(x);
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else {
resolve(x);
}
}
這一步驟非常簡(jiǎn)單,只要按照規(guī)范轉(zhuǎn)換成代碼即可。
最后,完整的Promise按照規(guī)范就實(shí)現(xiàn)完畢了,是的,規(guī)范里并沒有規(guī)定catch、Promise.resolve、Promise.reject、Promise.all等方法,接下來,我們就看一看Promise的這些常用方法。
Promise其他方法實(shí)現(xiàn)
1、catch方法
catch方法是對(duì)then方法的封裝,只用于接收reject(reason)中的錯(cuò)誤信息。因?yàn)樵?code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: 14px;border-radius: 4px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.047);font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace;word-break: break-all;">then方法中onRejected參數(shù)是可不傳的,不傳的情況下,錯(cuò)誤信息會(huì)依次往后傳遞,直到有onRejected函數(shù)接收為止,因此在寫promise鏈?zhǔn)秸{(diào)用的時(shí)候,then方法不傳onRejected函數(shù),只需要在最末尾加一個(gè)catch()就可以了,這樣在該鏈條中的promise發(fā)生的錯(cuò)誤都會(huì)被最后的catch捕獲到。
catch(onRejected) {
return this.then(null, onRejected);
}
2、done方法
catch在promise鏈?zhǔn)秸{(diào)用的末尾調(diào)用,用于捕獲鏈條中的錯(cuò)誤信息,但是catch方法內(nèi)部也可能出現(xiàn)錯(cuò)誤,所以有些promise實(shí)現(xiàn)中增加了一個(gè)方法done,done相當(dāng)于提供了一個(gè)不會(huì)出錯(cuò)的catch方法,并且不再返回一個(gè)promise,一般用來結(jié)束一個(gè)promise鏈。
done() {
this.catch(reason => {
console.log('done', reason);
throw reason;
});
}
3、finally方法
finally方法用于無論是resolve還是reject,finally的參數(shù)函數(shù)都會(huì)被執(zhí)行。
finally(fn) {
return this.then(value => {
fn();
return value;
}, reason => {
fn();
throw reason;
});
};
4、Promise.all方法
Promise.all方法接收一個(gè)promise數(shù)組,返回一個(gè)新promise2,并發(fā)執(zhí)行數(shù)組中的全部promise,所有promise狀態(tài)都為resolved時(shí),promise2狀態(tài)為resolved并返回全部promise結(jié)果,結(jié)果順序和promise數(shù)組順序一致。如果有一個(gè)promise為rejected狀態(tài),則整個(gè)promise2進(jìn)入rejected狀態(tài)。
static all(promiseList) {
return new Promise((resolve, reject) => {
const result = [];
let i = 0;
for (const p of promiseList) {
p.then(value => {
result[i] = value;
if (result.length === promiseList.length) {
resolve(result);
}
}, reject);
i++;
}
});
}
5、Promise.race方法
Promise.race方法接收一個(gè)promise數(shù)組, 返回一個(gè)新promise2,順序執(zhí)行數(shù)組中的promise,有一個(gè)promise狀態(tài)確定,promise2狀態(tài)即確定,并且同這個(gè)promise的狀態(tài)一致。
static race(promiseList) {
return new Promise((resolve, reject) => {
for (const p of promiseList) {
p.then((value) => {
resolve(value);
}, reject);
}
});
}
6、Promise.resolve方法/Promise.reject
Promise.resolve用來生成一個(gè)rejected完成態(tài)的promise,Promise.reject用來生成一個(gè)rejected失敗態(tài)的promise。
static resolve(value) {
let promise;
promise = new Promise((resolve, reject) => {
this.resolvePromise(promise, value, resolve, reject);
});
return promise;
}
static reject(reason) {
return new Promise((resolve, reject) => {
reject(reason);
});
}
常用的方法基本就這些,Promise還有很多擴(kuò)展方法,這里就不一一展示,基本上都是對(duì)then方法的進(jìn)一步封裝,只要你的then方法沒有問題,其他方法就都可以依賴then方法實(shí)現(xiàn)。
Promise面試相關(guān)
面試相關(guān)問題,筆者只說一下我司這幾年的情況,并不能代表全部情況,參考即可。Promise是我司前端開發(fā)職位,nodejs開發(fā)職位,全棧開發(fā)職位,必問的一個(gè)知識(shí)點(diǎn),主要問題會(huì)分布在Promise介紹、基礎(chǔ)使用方法以及深層次的理解三個(gè)方面,問題一般在3-5個(gè),根據(jù)面試者回答情況會(huì)適當(dāng)增減。
1、簡(jiǎn)單介紹下Promise。
Promise 是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和更強(qiáng)大。它由社區(qū)最早提出和實(shí)現(xiàn),ES6 將其寫進(jìn)了語(yǔ)言標(biāo)準(zhǔn),統(tǒng)一了用法,原生提供了Promise對(duì)象。有了Promise對(duì)象,就可以將異步操作以同步操作的流程表達(dá)出來,避免了層層嵌套的回調(diào)函數(shù)。此外,Promise對(duì)象提供統(tǒng)一的接口,使得控制異步操作更加容易。(當(dāng)然了也可以簡(jiǎn)單介紹promise狀態(tài),有什么方法,callback存在什么問題等等,這個(gè)問題是比較開放的)
-
提問概率:99% -
評(píng)分標(biāo)準(zhǔn):人性化判斷即可,此問題一般作為引入問題。 -
加分項(xiàng):熟練說出Promise具體解決了那些問題,存在什么缺點(diǎn),應(yīng)用方向等等。
2、實(shí)現(xiàn)一個(gè)簡(jiǎn)單的,支持異步鏈?zhǔn)秸{(diào)用的Promise類。
這個(gè)答案不是固定的,可以參考最簡(jiǎn)實(shí)現(xiàn) Promise,支持異步鏈?zhǔn)秸{(diào)用
-
提問概率:50%(手?jǐn)]代碼題,因?yàn)檫@類題目比較耗費(fèi)時(shí)間,一場(chǎng)面試并不會(huì)出現(xiàn)很多,所以出現(xiàn)頻率不是很高,但卻是必備知識(shí)) -
加分項(xiàng):基本功能實(shí)現(xiàn)的基礎(chǔ)上有 onResolved/onRejected函數(shù)異步調(diào)用,錯(cuò)誤捕獲合理等亮點(diǎn)。
3、Promise.then在Event Loop中的執(zhí)行順序。(可以直接問,也可以出具體題目讓面試者回答打印順序)
JS中分為兩種任務(wù)類型:macrotask和microtask,其中macrotask包含:主代碼塊,setTimeout,setInterval,setImmediate等(setImmediate規(guī)定:在下一次Event Loop(宏任務(wù))時(shí)觸發(fā));microtask包含:Promise,process.nextTick等(在node環(huán)境下,process.nextTick的優(yōu)先級(jí)高于Promise)Event Loop中執(zhí)行一個(gè)macrotask任務(wù)(棧中沒有就從事件隊(duì)列中獲?。﹫?zhí)行過程中如果遇到microtask任務(wù),就將它添加到微任務(wù)的任務(wù)隊(duì)列中,macrotask任務(wù)執(zhí)行完畢后,立即執(zhí)行當(dāng)前微任務(wù)隊(duì)列中的所有microtask任務(wù)(依次執(zhí)行),然后開始下一個(gè)macrotask任務(wù)(從事件隊(duì)列中獲?。?瀏覽器運(yùn)行機(jī)制可參考這篇文章
-
提問概率:75%(可以理解為4次面試中3次會(huì)問到,順便可以考察面試者對(duì) JS運(yùn)行機(jī)制的理解) -
加分項(xiàng):擴(kuò)展講述瀏覽器運(yùn)行機(jī)制。
4、闡述Promise的一些靜態(tài)方法。
Promise.deferred、Promise.all、Promise.race、Promise.resolve、Promise.reject等
-
提問概率:25%(相對(duì)基礎(chǔ)的問題,一般在其他問題回答不是很理想的情況下提問,或者為了引出下一個(gè)題目而提問) -
加分項(xiàng):越多越好
5、Promise存在哪些缺點(diǎn)。
1、無法取消Promise,一旦新建它就會(huì)立即執(zhí)行,無法中途取消。2、如果不設(shè)置回調(diào)函數(shù),Promise內(nèi)部拋出的錯(cuò)誤,不會(huì)反應(yīng)到外部。3、吞掉錯(cuò)誤或異常,錯(cuò)誤只能順序處理,即便在Promise鏈最后添加catch方法,依然可能存在無法捕捉的錯(cuò)誤(catch內(nèi)部可能會(huì)出現(xiàn)錯(cuò)誤) 4、閱讀代碼不是一眼可以看懂,你只會(huì)看到一堆then,必須自己在then的回調(diào)函數(shù)里面理清邏輯。
-
提問概率:25%(此問題作為提高題目,出現(xiàn)概率不高) -
加分項(xiàng):越多越合理越好(網(wǎng)上有很多說法,不一一佐證) (此題目,歡迎大家補(bǔ)充答案)
6、使用Promise進(jìn)行順序(sequence)處理。
1、使用async函數(shù)配合await或者使用generator函數(shù)配合yield。2、使用promise.then通過for循環(huán)或者Array.prototype.reduce實(shí)現(xiàn)。
function sequenceTasks(tasks) {
function recordValue(results, value) {
results.push(value);
return results;
}
var pushValue = recordValue.bind(null, []);
return tasks.reduce(function (promise, task) {
return promise.then(() => task).then(pushValue);
}, Promise.resolve());
}
-
提問概率:90%(我司提問概率極高的題目,即能考察面試者對(duì) promise的理解程度,又能考察編程邏輯,最后還有bind和reduce等方法的運(yùn)用) -
評(píng)分標(biāo)準(zhǔn):說出任意解決方法即可,其中只能說出 async函數(shù)和generator函數(shù)的可以得到20%的分?jǐn)?shù),可以用promise.then配合for循環(huán)解決的可以得到60%的分?jǐn)?shù),配合Array.prototype.reduce實(shí)現(xiàn)的可以得到最后的20%分?jǐn)?shù)。
7、如何停止一個(gè)Promise鏈?
在要停止的promise鏈位置添加一個(gè)方法,返回一個(gè)永遠(yuǎn)不執(zhí)行resolve或者reject的Promise,那么這個(gè)promise永遠(yuǎn)處于pending狀態(tài),所以永遠(yuǎn)也不會(huì)向下執(zhí)行then或catch了。這樣我們就停止了一個(gè)promise鏈。
Promise.cancel = Promise.stop = function() {
return new Promise(function(){})
}
-
提問概率:50%(此問題主要考察面試者羅輯思維) (此題目,歡迎大家補(bǔ)充答案)
8、Promise鏈上返回的最后一個(gè)Promise出錯(cuò)了怎么辦?
catch在promise鏈?zhǔn)秸{(diào)用的末尾調(diào)用,用于捕獲鏈條中的錯(cuò)誤信息,但是catch方法內(nèi)部也可能出現(xiàn)錯(cuò)誤,所以有些promise實(shí)現(xiàn)中增加了一個(gè)方法done,done相當(dāng)于提供了一個(gè)不會(huì)出錯(cuò)的catch方法,并且不再返回一個(gè)promise,一般用來結(jié)束一個(gè)promise鏈。
done() {
this.catch(reason => {
console.log('done', reason);
throw reason;
});
}
-
提問概率:90%(同樣作為出題率極高的一個(gè)題目,充分考察面試者對(duì) promise的理解程度) -
加分項(xiàng):給出具體的 done()方法代碼實(shí)現(xiàn)
9、Promise存在哪些使用技巧或者最佳實(shí)踐?
1、鏈?zhǔn)?code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: 14px;border-radius: 4px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.047);font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace;word-break: break-all;">promise要返回一個(gè)promise,而不只是構(gòu)造一個(gè)promise。2、合理的使用Promise.all和Promise.race等方法。3、在寫promise鏈?zhǔn)秸{(diào)用的時(shí)候,then方法不傳onRejected函數(shù),只需要在最末尾加一個(gè)catch()就可以了,這樣在該鏈條中的promise發(fā)生的錯(cuò)誤都會(huì)被最后的catch捕獲到。如果catch()代碼有出現(xiàn)錯(cuò)誤的可能,需要在鏈?zhǔn)秸{(diào)用的末尾增加done()函數(shù)。
-
提問概率:10%(出題概率極低的一個(gè)題目) -
加分項(xiàng):越多越好
(此題目,歡迎大家補(bǔ)充答案)
至此,我司關(guān)于Promise的一些面試題目就列舉完畢了,有些題目的答案是開放的,歡迎大家一起補(bǔ)充完善??偨Y(jié)起來,Promise作為js面試必問部分還是相對(duì)容易掌握并通過的。
總結(jié)
Promise作為所有js開發(fā)者的必備技能,其實(shí)現(xiàn)思路值得所有人學(xué)習(xí),通過這篇文章,希望小伙伴們?cè)谝院缶幋a過程中能更加熟練、更加明白的使用Promise。
逆鋒起筆是一個(gè)專注于程序員圈子的技術(shù)平臺(tái),你可以收獲最新技術(shù)動(dòng)態(tài)、最新內(nèi)測(cè)資格、BAT等大廠大佬的經(jīng)驗(yàn)、增長(zhǎng)自身、學(xué)習(xí)資料、職業(yè)路線、賺錢思維,微信搜索逆鋒起筆關(guān)注!
Java 初級(jí)和高級(jí)面試知識(shí)點(diǎn)準(zhǔn)備
點(diǎn)贊是最大的支持

