跳到主要內容
黯羽輕揚每天積累一點點

動手實現 promise

免費2015-12-13#JS#Tool#promise polyfill#JavaScript Promise Polyfill#promise workaround#promise兼容方案#promise内部原理剖析

js 的 promise 不過是 promise A+ 規範的一種實現,自己動手當然也能實現,也就得到了所謂的 polyfill

##一。介面分析

###1. 核心功能

  • resolve(value)/reject(err)

建構函式引數 resolve/reject 雖然不是靜態方法也不是例項方法,卻是資料入口,所以是 promise API 的核心,promise 機制的大部分秘密都在裡面,所有公開介面都是在此基礎上工作的

  • then(onFulfilled, onRejected)

resolve/reject 外,then 絕對是最重要的,其它所有例項方法/靜態方法都不是必要的,而 then 卻是必不可少的。換言之,能 new Promise 還支援 then,整個 promise 機制就完成了,其它介面都只是錦上添花的東西,讓 promise 變得更易用

###2. 例項方法

  • catch(onRejected)

等價於 promise.then(null, onRejected),使用者可以少寫一點程式碼,同時賦予了語義(catch error)

###3. 靜態方法

  • Promise.resolve(value)

提供一個 wrapper,不確定 value 是不是 Promise 例項時,可以用該方法獲取一個被包裝好的 Promise 例項,內部機制比較複雜,詳細資訊請參考 [6.Promise.resolve(value) 解決](/articles/完全理解 promise/#articleHeader14)

  • Promise.reject(reason)

同樣提供一個 wrapper,是建立否定 Promise 的快捷方式,內部機制比 Promise.resolve(value) 簡單很多

  • Promise.all(iterable)

要麼全都正常完成,要麼因錯誤中斷,未完成的全都停下來

  • Promise.race(iterable)

「賽跑」,不論結果是肯定還是否定,只要最快的

###4. 高階功能

  • 結果自動後拋

      // 錯誤後拋
      new Promise(function(resolve, reject) {reject('x')})
      .then(null)
      .then(null)
      .catch(function(err) {console.log(err)})
      // 值後拋
      new Promise(function(resolve, reject) {resolve('x')})
      .catch(null)
      .catch(null)
      .then(function(future) {console.log(future)})
    

    錯誤被拋給了最後的 catch,只要沒被「吃掉」就自動後拋,最後要麼被「吃掉」,要麼拋至頂層報錯

  • 結果自動外拋

      // 外層 then 能夠拿到內層 future 和 error
      new Promise(function(resolve, reject) {
          resolve(new Promise(function(resolve, reject) {
              resolve(12);
              // reject(new Error('reject in nested Promise'));
          }).then(function(future) {
              return future + 1;
          }, function(error) {
              return new Error('reject again in nested Promise');
          }));
      }).then(onFulfilled, onRejected);
    

    外層的 then 能夠拿到內層 future 和 error,避免手動維護狀態,也有利於錯誤集中處理

##二。難點

難點在於 promise 機制本身是非同步的,示例如下:

var p = new Promise(function(resolve, reject) {resolve(1)}).then(function(future) {return 2;});
// 立即輸出 p
console.log(p);
// 1s 後輸出 p
setTimeout(function() {
    console.log(p);
}, 1000);

立即輸出的結果是:

// console
Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined}

1s 後輸出的結果是:

// console
Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: 2}

在這種非同步機制下,returnthrow 都成了問題,例如:

// 非同步
function nextTick(fn) {
    setTimeout(fn, 0);
}
// test
try {
    nextTick(function() {
        throw new Error('x');
    });
}
catch (err) {
    console.log('err: ' + err.message);
}

結果是無法 catch 錯誤,x 被拋到了頂層,顯示在控制檯了。很容易想到把 try..catch 放進去,嗯,錯誤確實 catch 住了,可是要如何通知外層?

returnthrow ���是傳值的手段,在非同步環境下無法直接使用,需要我們自行實現一種傳值機制,這就是難點

P.S. 有一篇文章中提到了 try...catch 的缺點,詳細資訊請檢視 前端程式碼異常監控

##三。專案地址

地址:ayqy / myPromise - 程式碼託管 - 開源中國社群

注意:目前版本(v0.1.0 2015/12/13)存在很大問題,千萬不要直接使用,因為目前內部機制是同步的

P.S. 版本更新到可用時,筆者會在此處說明,在這之前,建議使用更靠譜的 taylorhakes/promise-polyfill · GitHub,很輕巧的實現

###參考資料

評論

暫無評論,快來發表你的看法吧

提交評論