A promise is an object that may produce a single value some time in the future: either a resolved value, or a reason that it’s not resolved (e.g., a network error occurred). A promise may be in one of 3 possible states: fulfilled, rejected, or pending. Promise users can attach callbacks to handle the fulfilled value or the reason for rejection.javascript
關鍵語句: Promise 是一個在未來某個時刻產生一個單一結果的對象. 通俗一點來講, Promise 表明了一個值, 可是這個值咱們並不肯定何時會被返回.前端
A promise is an object that may produce a single value some time in the future.java
Promise 在 1980 年代被建立出來node
在 1988 年正式得名: Promisegit
已經有不少人瞭解到了 Promise, 可是人們仍是堅持使用 node.js 中提倡的以回調函數首個參數傳 error 對象的方式處理異步代碼.github
Dojo 首次大規模的使用了 Promise , 相應的 Promise/A 被提出用以規範 Promise 的實現api
JQuery 開始使用 Promise 並真正使 Promise 廣爲人知promise
JQuery 沒有實現部分 Promise 的功能, 這也致使了 Promie/A+ 標準的產生閉包
ES6 正式引入了 Promise,而且和已有的實現了 Promise/A 規範的 library 相兼容dom
thenable
對象, 也就是說 Promise 有一個 .then()
方法首先, 讓咱們看看一段最普通的異步代碼:
// 異步方法定義 var basicAsyncFunc = function(callback) { setTimeout(function() { var randomNumber = Math.random() if (randomNumber > 0.5) { callback(null, randomNumber) } else { callback(new Error('bad luck...')) } }, 1000) } // 異步方法調用 basicAsyncFunc((err, result) => { if (err) { console.log(`the reason fail is: ${err}`) return } console.log(`success get result: ${result}`) })
按照 Promise 的規範定義, 理想中 Promise 的調用方式爲:
// Promise 形式的異步方法定義 var promiseAsyncFunc = function() {} // Promise 形式的異步方法調用 promiseAsyncFunc.then( data => { console.log(`success get result: ${data}`) }, err => { console.log(`the reason fail is: ${err}`) } )
按照這個理想當中的調用方式, 讓咱們寫出初版代碼.
// Promise 形式的異步方法定義 var promiseAsyncFunc = function() { var fulfillCallback var rejectCallback setTimeout(() => { var randomNumber = Math.random() if (randomNumber > 0.5) fulfillCallback(randomNumber) else rejectCallback(randomNumber) }, 1000) return { then: function(_fulfillCallback, _rejectCallback) { fulfillCallback = _fulfillCallback rejectCallback = _rejectCallback } } } // Promise 形式的異步方法調用 promiseAsyncFunc().then(fulfillCallback, rejectCallback)
咱們的思路是在 .then()
方法中, 將 fullfill 和 reject 結果的回調函數保存下來, 而後在異步方法中調用. 由於是異步調用, 根據 event-loop 的原理, promiseAsyncFunc().then(fulfillCallback, rejectCallback)
傳入的 callback 在異步調用結束時必定是已經賦值過了.
當前咱們的實現 Promise 中,異步邏輯代碼和 Promise 的代碼是雜糅在一塊兒的,讓咱們將其區分開:
var promiseAsyncFunc = function() { var fulfillCallback var rejectCallback return { fulfill: function(value) { if (fulfillCallback && typeof fulfillCallback === 'function') { fulfillCallback(value) } }, reject: function(err) { if (rejectCallback && typeof rejectCallback === 'function') { rejectCallback(err) } }, then: function(_fulfillCallback, _rejectCallback) { fulfillCallback = _fulfillCallback rejectCallback = _rejectCallback } } } let ownPromise = function(asyncCall) { let promise = promiseAsyncFunc() asyncCall(promise.fulfill, promise.reject) return promise } // Promise 形式的異步方法調用 ownPromise(function(fulfill, reject) { setTimeout(() => { var randomNumber = Math.random() if (randomNumber > 0.5) fulfill(randomNumber) else reject(randomNumber) }, 1000) })
咱們新定義了一個方法 ownPromise()
用於建立 Promise,並在promiseAsyncFunc()
中暴露出 fulfill
和 reject
接口方便異步代碼去調用。
這裏有一個問題,咱們在調用 ownPromise()
後獲得了 promise 實例,此時咱們能夠直接調用 fulfill()
,reject()
這兩個方法,而理論上咱們應該只應暴露 promise 的then()
方法。因此咱們利用閉包將這兩個方法隱藏:
var promiseAsyncFunc = function() { var fulfillCallback var rejectCallback return { fulfill: function(value) { if (fulfillCallback && typeof fulfillCallback === 'function') { fulfillCallback(value) } }, reject: function(err) { if (rejectCallback && typeof rejectCallback === 'function') { rejectCallback(err) } }, promise: { then: function(_fulfillCallback, _rejectCallback) { fulfillCallback = _fulfillCallback rejectCallback = _rejectCallback } } } } let ownPromise = function(asyncCall) { let defer = promiseAsyncFunc() asyncCall(defer.fulfill, defer.reject) return defer.promise } // Promise 形式的異步方法調用 ownPromise(function(fulfill, reject) { setTimeout(() => { var randomNumber = Math.random() if (randomNumber > 0.5) fulfill(randomNumber) else reject(randomNumber) }, 1000) })
爲了實現規範中對於 Promise 狀態變化的要求, 咱們須要爲 Promise 加入狀態管理, 這一步較爲簡單, 讓咱們看代碼:
const PENDING = Symbol('pending') const FULFILLED = Symbol('fulfilled') const REJECTED = Symbol('rejected') // Promise 形式的異步方法定義 var promiseAsyncFunc = function() { var status = PENDING var fulfillCallback var rejectCallback return { fulfill: function(value) { if (status !== PENDING) return if (typeof fulfillCallback === 'function') { fulfillCallback(value) status = FULFILLED } }, reject(error) { if (status !== PENDING) return if (typeof rejectCallback === 'function') { rejectCallback(error) status = REJECTED } }, promise: { then: function(_fulfillCallback, _rejectCallback) { fulfillCallback = _fulfillCallback rejectCallback = _rejectCallback } } } } let ownPromise = function(asyncCall) { let defer = promiseAsyncFunc() asyncCall(defer.fulfill, defer.reject) return defer.promise } // Promise 形式的異步方法調用 ownPromise(function(fulfill, reject) { setTimeout(() => { var randomNumber = Math.random() if (randomNumber > 0.5) fulfill(randomNumber) else reject(randomNumber) }, 1000) }).then(data => console.log(data), err => console.log(err))
這段代碼中咱們用到了 Symbol 來表示狀態常量, 對 Symbol 不瞭解的同窗能夠看這裏
爲了判斷 Promise 的狀態, 咱們加入了 fulfill
和 reject
兩個方法。並在其中判斷 promise 當前狀態。若是不是 pending 狀態則直接 return(由於 Promise 狀態只可能改變一次)。
如今咱們的 promise 實現了對狀態控制的規範:
可是咱們的 Promise 有一個問題: promise 的值沒有被保存下來。若是 promise 在異步調用完成以後才被調用 .then()
方法,則咱們沒法把異步調用的結果傳遞給回調函數。爲此咱們須要爲 Promise 加一個 value 字段:
咱們爲 promise 加入 value 字段,用於保存 Promise 的執行結果。
// Promise 形式的異步方法定義 var promiseAsyncFunc = function() { var status = PENDING var fulfillCallback var rejectCallback var value return { fulfill: function(_value) { if (status !== PENDING) return value = _value status = FULFILLED if (typeof fulfillCallback === 'function') { fulfillCallback(value) } }, reject(error) { if (status !== PENDING) return value = error status = REJECTED if (typeof rejectCallback === 'function') { rejectCallback(error) } }, promise: { then: function(_fulfillCallback, _rejectCallback) { fulfillCallback = _fulfillCallback rejectCallback = _rejectCallback } } } }
這裏咱們又發現一個問題,若是一個 Promise 已是fulfill
或reject
狀態。咱們再調用 then()
方法時,傳入的回調方法永遠不會被調用(由於 status 已經不是 pending)。
因此咱們須要在 then()
方法中對其狀態進行判斷:
// Promise 形式的異步方法定義 var promiseAsyncFunc = function() { var status = PENDING var fulfillCallback var rejectCallback var value return { fulfill: function(_value) { if (status !== PENDING) return value = _value status = FULFILLED if (typeof fulfillCallback === 'function') { fulfillCallback(value) } }, reject(error) { if (status !== PENDING) return value = error status = REJECTED if (typeof rejectCallback === 'function') { rejectCallback(error) } }, promise: { then: function(_fulfillCallback, _rejectCallback) { if (status === REJECTED) { _rejectCallback(value) return } if (status === FULFILLED) { _fulfillCallback(value) return } fulfillCallback = _fulfillCallback rejectCallback = _rejectCallback } } } }
爲了支持鏈式調用,.then()
方法的返回值必須是用 thenable
(根據 Promise/A+ 規範, .then()
方法的返回值須要是一個新的 Promise)
爲此咱們加入一個工具方法 makeThenable()
。若是傳入的 value 自己就有 then()
方法,則直接返回 value。不然返回一個有 then()
方法的對象。 在該對象的 then()
方法中,咱們根據 promise 的狀態,調用不一樣的回調方法生成新的 value。
function makeThenable(value, status){ if(value && typeof value.then === 'function'){ return value } if(status === FULFILLED){ return { then: function(fulfillCallback, rejectCallback){ return makeThenable(fulfillCallback(value), FULFILLED) } } } if(status === REJECTED) { return { then: function(fulfillCallback, rejectCallback){ return makeThenable(rejectCallback(value), FULFILLED) } } } }
有了以上的 makeThenable()
方法,咱們能夠在 promise 的fulfill()
,reject()
回將 value 設置爲 thenable
:
var promiseAsyncFunc = function() { var status = PENDING var fulfillCallback var rejectCallback var value return { fulfill: function(_value) { if (status !== PENDING) return value = makeThenable(_value, FULFILLED) // 保證當前promise的value爲 thenable status = FULFILLED if (typeof fulfillCallback === 'function') { value.then(fulfillCallback) } }, reject(error) { if (status !== PENDING) return value = makeThenable(error, REJECTED) 、、 // 保證當前value爲 thenable status = REJECTED if (typeof rejectCallback === 'function') { value.then(null, rejectCallback) } }, promise: { then: function(){} } } }
接下來讓咱們看 then()
方法。爲了返回一個新的 promise,咱們首先得建立一個新的 promise。其次當前 promise 在fulfill()
或 reject()
時,應該調用新的 promise 的fullfill()
或 reject()
方法。因此咱們在將 fulfullCallback
和rejectCallback
賦值給當前 promise 時,將其包裝一下。代碼以下:
promise: { then: function(_fulfillCallback, _rejectCallback) { let newPromiseAsyncFunc = promiseAsyncFunc() let fulfillFunc = function(value) { newPromiseAsyncFunc.fulfill(_fulfillCallback(value)) } let rejectFunc = function(err) { newPromiseAsyncFunc.fulfill(_rejectCallback(err)) } if (status === PENDING) { fulfillCallback = fulfillFunc rejectCallback = rejectFunc } else { value.then(fulfillFunc, rejectFunc) } return newPromiseAsyncFunc.promise } }
如此,咱們變獲得了一個能夠鏈式調用的 promise。讓咱們來測試一下:
const PENDING = Symbol('pending') const FULFILLED = Symbol('fulfilled') const REJECTED = Symbol('rejected') function makeThenable(value, status) { if (value && typeof value.then === 'function') { return value } if (status === FULFILLED) { return { then: function(fulfillCallback, rejectCallback) { return makeThenable(fulfillCallback(value), FULFILLED) } } } if (status === REJECTED) { return { then: function(fulfillCallback, rejectCallback) { return makeThenable(rejectCallback(value), FULFILLED) } } } } // Promise 形式的異步方法定義 var promiseAsyncFunc = function() { var status = PENDING var fulfillCallback var rejectCallback var value return { fulfill: function(_value) { if (status !== PENDING) return value = makeThenable(_value, FULFILLED) status = FULFILLED if (typeof fulfillCallback === 'function') { value.then(fulfillCallback) } }, reject(error) { if (status !== PENDING) return value = makeThenable(error, REJECTED) status = REJECTED if (typeof rejectCallback === 'function') { value.then(null, rejectCallback) } }, promise: { then: function(_fulfillCallback, _rejectCallback) { let newPromiseAsyncFunc = promiseAsyncFunc() let fulfillFunc = function(value) { newPromiseAsyncFunc.fulfill(_fulfillCallback(value)) } let rejectFunc = function(err) { newPromiseAsyncFunc.fulfill(_rejectCallback(err)) } if (status === PENDING) { fulfillCallback = fulfillFunc rejectCallback = rejectFunc } else { value.then(fulfillFunc, rejectFunc) } return newPromiseAsyncFunc.promise } } } } let ownPromise = function(asyncCall) { let defer = promiseAsyncFunc() asyncCall(defer.fulfill, defer.reject) return defer.promise } let testChainedPromise = ownPromise(function(fulfill, reject) { setTimeout(() => { var randomNumber = Math.random() if (randomNumber > 0.5) fulfill(randomNumber) else reject(randomNumber) }, 1000) }) .then( data => { console.log(data) return 'return value in then1 fulfill' }, err => { console.log(err) return 'return value in then1 reject' } ) .then( data => { console.log(data) return 'return value in then2 fulfill' }, err => { console.log(err) return 'return value in then2 reject' } ) .then( data => { console.log(data) }, err => { console.log(err) } ) /** console output: 0.9931984611850693 return value in then1 fulfill return value in then2 fulfill */
這裏咱們只對異步調用和fulfill 回調中拋出的 error 進行處理。
首先是異步調用部分,咱們將其 try catch
起來,在發生異常時調用 reject 方法,並將異常做爲參數傳入。
let ownPromise = function(asyncCall) { let defer = promiseAsyncFunc() try { asyncCall(defer.fulfill, defer.reject) } catch (e) { defer.reject(e) } return defer.promise }
而後是 fulfill
中可能出現的異常。咱們對fulfillCallback(value)
可能出現的異常進行捕獲,並將異常傳遞給rejectCallback
。
function makeThenable(value, status) { if (value && typeof value.then === 'function') { return value } if (status === FULFILLED) { return { then: function(fulfillCallback, rejectCallback) { try { let newValue = fulfillCallback(value) return makeThenable(newValue, FULFILLED) } catch (e) { return makeThenable(rejectCallback(e), FULFILLED) } } } } if (status === REJECTED) { return { then: function(fulfillCallback, rejectCallback) { return makeThenable(rejectCallback(value), FULFILLED) } } } }
最後讓咱們對完整的代碼進行測試:
const PENDING = Symbol('pending') const FULFILLED = Symbol('fulfilled') const REJECTED = Symbol('rejected') function makeThenable(value, status) { if (value && typeof value.then === 'function') { return value } if (status === FULFILLED) { return { then: function(fulfillCallback, rejectCallback) { try { let newValue = fulfillCallback(value) return makeThenable(newValue, FULFILLED) } catch (e) { return makeThenable(rejectCallback(e), FULFILLED) } } } } if (status === REJECTED) { return { then: function(fulfillCallback, rejectCallback) { return makeThenable(rejectCallback(value), FULFILLED) } } } } // Promise 形式的異步方法定義 var promiseAsyncFunc = function() { var status = PENDING var fulfillCallback var rejectCallback var value return { fulfill: function(_value) { if (status !== PENDING) return value = makeThenable(_value, FULFILLED) status = FULFILLED if (typeof fulfillCallback === 'function') { value.then(fulfillCallback) } }, reject(error) { if (status !== PENDING) return value = makeThenable(error, REJECTED) if (typeof rejectCallback === 'function') { value.then(null, rejectCallback) } status = REJECTED }, promise: { then: function(_fulfillCallback, _rejectCallback) { let newPromiseAsyncFunc = promiseAsyncFunc() let fulfillFunc = function(value) { newPromiseAsyncFunc.fulfill(_fulfillCallback(value)) } let rejectFunc = function(err) { newPromiseAsyncFunc.fulfill(_rejectCallback(err)) } if (status === PENDING) { fulfillCallback = fulfillFunc rejectCallback = rejectFunc } else { value.then(fulfillFunc, rejectFunc) } return newPromiseAsyncFunc.promise } } } } let ownPromise = function(asyncCall) { let defer = promiseAsyncFunc() try { asyncCall(defer.fulfill, defer.reject) } catch (e) { defer.reject(e) } return defer.promise } let testChainedPromise = ownPromise(function(fulfill, reject) { throw Error('here is an error in asyncCall') setTimeout(() => { var randomNumber = Math.random() if (randomNumber > 0.5) fulfill(randomNumber) else reject(randomNumber) }, 1000) }) .then( data => { console.log(data) return 'return value in then1 fulfill' }, err => { console.log(err.message) return 'return value in then1 reject' } ) .then( data => { console.log(data) throw Error('here is an error in fulfill1') return 'return value in then2 fulfill' }, err => { console.log(err.message) return 'return value in then2 reject' } ) .then( data => { console.log(data) }, err => { console.log(err.message) } ) // console out: Error: here is an error in asyncCall return value in then1 reject Error: here is an error in fulfill1 return value in then2 reject
以上就是咱們對於 Promise 的一個簡單的實現,實現思路主要參考了 Q-A promise library for javascript。該實現的 Promise 功能較爲簡陋,僅實現了部分 api/規範。有任何意見和建議歡迎在評論區交流 ;)
對於 Promise 使用以及error handle 的講解:
https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-promise-27fc71e77261
Promise 實現庫之一: Q 對於 Promise 實現的講解:
這裏是個人 前端、D3.js 、 數據可視化 的 github 地址, 歡迎 star & fork :tada:
郵箱: ssthouse@163.com