「每日一題」面試官問你對Promise的理解?可能是需要你能手動實現各個特性

saucxs發表於2020-12-30

關注「鬆寶寫程式碼」,精選好文,每日一題

加入我們一起學習,day day up

作者:saucxs | songEagle

來源:原創

一、前言

2020.12.23日剛立的flag,每日一題,題目型別不限制,可以是:演算法題,面試題,闡述題等等。

每日一題

往期「每日一題」:

接下來是第3道:談談你對 promise 的理解?

二、談談你對 promise 的理解?

1、我們簡單概括一下promise

Promise 是 ES6 新增的語法,解決了回撥地獄的問題。

無論是ES6的Promise也好,jQuery的Promise也好,不同的庫有不同的實現,但是大家遵循的都是同一套規範,所以,Promise並不指特定的某個實現,它是一種規範,是一套處理JavaScript非同步的機制。

Promise 本質上就是一個繫結了回撥的物件,而不是將回撥傳回函式內部。

所以,Promise在一定程度上解決了回撥函式的書寫結構問題,但回撥函式依然在主流程上存在,只不過都放到了then(...)裡面,和我們大腦順序線性的思維邏輯還是有出入的。

2、我們說一下promise相關規範

  • 可以把 Promise 看成一個狀態機。初始是 pending 狀態,可以通過函式 resolve 和 reject ,將狀態轉變為 resolved 或者 rejected 狀態,狀態一旦改變就不能再次變化。

  • then 函式會返回一個 Promise 例項,並且該返回值是一個新的例項而不是之前的例項。因為 Promise 規範規定除了 pending 狀態,其他狀態是不可以改變的,如果返回的是一個相同例項的話,多個 then 呼叫就失去意義了。

  • then 方法可以被同一個 promise 呼叫多次。

  • 值穿透

三、promise 是如何實現的?

1、Promise的簡單使用

Promise的簡單使用

我們通過這種使用構建Promise實現的最初版本

2、Promise的大致框架

大致框架有了,但是Promise狀態,resolve函式,reject函式,以及then等回撥沒有詳細處理

Promise的大致框架

3、Promise的鏈式儲存

我們先看一個例子:

Promise的鏈式儲存

每間隔1秒列印一個數字,哈哈,這個不是真實的間隔1秒,汪汪,

這個的輸出是啥?

列印順序:1、2、3

這裡我們能確認的是:

  • 讓a,b,c的只能在then的回撥接收到
  • 在連續的非同步呼叫中,如何保證非同步函式的執行順序

Promise一個常見的需求就是連續執行兩個或者多個非同步操作,這種情況下,每一個後來的操作都在前面的操作執行成功之後,帶著上一步操作所返回的結果開始執行。這裡用setTimeout來處理.

Promise的鏈式儲存

4、Promise的狀態機制和執行順序

為了保證Promise的非同步操作時的順序執行,這裡給Promise加上狀態機制

Promise的狀態機制和執行順序

5、Promise的遞迴執行

每個Promise後面連結一個物件,該物件包含onresolved,onrejected,子promise三個屬性.

當父Promise 狀態改變完畢,執行完相應的onresolved/onrejected的時候,拿到子promise,在等待這個子promise狀態改變,在執行相應的onresolved/onrejected。依次迴圈直到當前promise沒有子promise。

Promise的遞迴執行

6、Promise的異常處理

每個Promise後面連結一個物件,該物件包含onresolved,onrejected,子promise三個屬性.

當父Promise 狀態改變完畢,執行完相應的onresolved/onrejected的時候,拿到子promise,在等待這個子promise狀態改變,在執行相應的onresolved/onrejected。依次迴圈直到當前promise沒有子promise。

Promise的異常處理

7、Promise的then的實現

then 方法是 Promise 的核心,這裡做一下詳細介紹。

promise.then(onFulfilled, onRejected)

一個 Promise 的then接受兩個引數: onFulfilled和onRejected(都是可選引數,並且為函式,若不是函式將被忽略)

  • onFulfilled 特性:

當 Promise 執行結束後其必須被呼叫,其第一個引數為 promise 的終值,也就是 resolve 傳過來的值
在 Promise 執行結束前不可被呼叫
其呼叫次數不可超過一次

  • onRejected 特性

當 Promise 被拒絕執行後其必須被呼叫,第一個引數為 Promise 的拒絕原因,也就是reject傳過來的值
在 Promise 執行結束前不可被呼叫
其呼叫次數不可超過一次

  • 呼叫時機

onFulfilled 和 onRejected 只有在執行環境堆疊僅包含平臺程式碼時才可被呼叫(平臺程式碼指引擎、環境以及 promise 的實施程式碼)

  • 呼叫要求

onFulfilled 和 onRejected 必須被作為函式呼叫(即沒有 this 值,在 嚴格模式(strict) 中,函式 this 的值為 undefined ;在非嚴格模式中其為全域性物件。)

  • 多次呼叫

then 方法可以被同一個 promise 呼叫多次

當 promise 成功執行時,所有 onFulfilled 需按照其註冊順序依次回撥
當 promise 被拒絕執行時,所有的 onRejected 需按照其註冊順序依次回撥

  • 返回

then方法會返回一個Promise,關於這一點,Promise/A+標準並沒有要求返回的這個Promise是一個新的物件,但在Promise/A標準中,明確規定了then要返回一個新的物件,目前的Promise實現中then幾乎都是返回一個新的Promise(詳情)物件,所以在我們的實現中,也讓then返回一個新的Promise物件。

promise2 = promise1.then(onFulfilled, onRejected);
  • 如果 onFulfilled 或者 onRejected 返回一個值 x ,則執行下面的 Promise 解決過程:[[Resolve]](promise2, x)
  • 如果 onFulfilled 或者 onRejected 丟擲一個異常 e ,則 promise2 必須拒絕執行,並返回拒因 e
  • 如果 onFulfilled 不是函式且 promise1 成功執行, promise2 必須成功執行並返回相同的值
  • 如果 onRejected 不是函式且 promise1 拒絕執行, promise2 必須拒絕執行並返回相同的拒因

不論 promise1 被 reject 還是被 resolve , promise2 都會被 resolve,只有出現異常時才會被 rejected。

每個Promise物件都可以在其上多次呼叫then方法,而每次呼叫then返回的Promise的狀態取決於那一次呼叫then時傳入引數的返回值,所以then不能返回this,因為then每次返回的Promise的結果都有可能不同。

Promise的then的實現

8、Promise的值穿透

Promise的then的實現

我們來看一下這個題的輸出:

Promise的then的實現

最終打結果是1而不是2.

我們再來看一下這個題的輸出:

Promise的then的實現

各種福利

鬆寶寫程式碼

「鬆寶寫程式碼」公眾號:開發知識體系構建,技術分享,專案實戰,實驗室,每日一題,帶你一起學習新技術,總結學習過程,讓你進階到高階資深工程師,學習專案管理,思考職業發展,生活感悟,充實中成長起來。問題或建議,請公眾號留言。

1、位元組內推福利

回覆「校招」獲取內推碼

回覆「社招」獲取內推

回覆「實習生」獲取內推

後續會有更多福利

2、學習資料福利

回覆「演算法」獲取演算法學習資料

3、每日一題

相關文章