then/promise
項目是基於Promises/A+
標準實現的Promise
庫,從這個項目當中,咱們來看Promise
的原理是什麼,它是如何作到的,從而更加熟悉Promise
前端
從index.js當中知道,它是先引出了./core.js
,隨後各自執行了其餘文件的代碼,經過requeire
的方法。node
咱們首先先想一下最基礎的promise用法git
new Promise((resolve, reject) => { resolve(4); }).then(res => { console.log(res); // export 4 });
標準中規定:es6
Pending
,在被 resolve
或 reject
時,狀態變爲 Fulfilled
或 Rejected
resolve
接收成功的數據,reject
接收失敗或錯誤的數據Promise
對象必須有一個 then
方法,且只接受兩個可函數參數 onFulfilled
、onRejected
index.js
github
'use strict'; module.exports = require('./core.js'); require('./done.js'); require('./finally.js'); require('./es6-extensions.js'); require('./node-extensions.js'); require('./synchronous.js');
咱們先看src/core.js
c#
function Promise(fn) { // 判斷 this必定得是object否則就會報錯,這個方法必定得要new出來 if (typeof this !== 'object') { throw new TypeError('Promises must be constructed via new'); } // 判斷fn 必定得是一個函數 if (typeof fn !== 'function') { throw new TypeError('Promise constructor\'s argument is not a function'); } this._deferredState = 0; this._state = 0; this._value = null; this._deferreds = null; if (fn === noop) return; // 最終doResolve很關鍵 doResolve(fn, this); }
Promise
是一個構造方法,開始時,它進行了校驗,確保了fn
是一個函數,隨後對一些變量進行了初始化,最後執行了doResolve()
數組
咱們接着看doResolve
這個方法。promise
/** * Take a potentially misbehaving resolver function and make sure * onFulfilled and onRejected are only called once. * * Makes no guarantees about asynchrony. */ // // 確保`onFulfilled`和`onRejected`方法只調用一次 // 不保證異步 function doResolve(fn, promise) { var done = false; var res = tryCallTwo(fn, function (value) { // 若是done 爲true 則return if (done) return; done = true; // 回調執行 resolve() resolve(promise, value); }, function (reason) { // 若是done 爲true 則return if (done) return; done = true; reject(promise, reason); }); // res爲truCallTwo()的返回值 // 若是done沒有完成 而且 res 是 `IS_ERROR`的狀況下 // 也會執行reject(),同時讓done完成 if (!done && res === IS_ERROR) { done = true; reject(promise, LAST_ERROR); } }
doResolve
最關鍵的是執行了tryCallTwo
方法,這個方法的第二,第三個參數都是回調,當執行回調後,done
爲true,同時各自會執行resolve()
或者reject()
方法。最後當tryCallTwo
的返回值爲IS_ERROR
時,也會執行reject()
方法。app
咱們先來看一下tryCallTwo
方法異步
function tryCallTwo(fn, a, b) { try { fn(a, b); } catch (ex) { LAST_ERROR = ex; return IS_ERROR; } }
fn
實際就是Promise
初始化時的匿名函數(resolve, reject) => {}
,a
,b
則表明的是resolve()
和reject()
方法,當咱們正常執行完promise
函數時,則執行的是resolve
則在doResolve中
,咱們當時執行的第二個參數被回調,若是報錯,reject()
被執行,則第二個參數被回調。最後捕獲了異常,當發生了報錯時,會return IS_ERROR
,非報錯時會return undinfed
再回到剛纔的doResolve
方法,當執行了第二個參數的回調以後,會執行resolve
方法
function resolve(self, newValue) { // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure // 不能吃傳遞本身 if (newValue === self) { // 報錯 return reject( self, new TypeError('A promise cannot be resolved with itself.') ); } // promise做爲參數 if ( newValue && (typeof newValue === 'object' || typeof newValue === 'function') ) { // 獲取它的promise方法 讀取newValue.then var then = getThen(newValue); if (then === IS_ERROR) { // 若是then IS_ERROR return reject(self, LAST_ERROR); } if ( // 若是then是self的then // 而且Promise then === self.then && // newValue 屬於Promise newValue instanceof Promise ) { // _state爲3 // 通常then以後走這裏 // 執行then(newValue)返回了promise self._state = 3; // selft.value爲newValue self._value = newValue; // 當state爲3時執行 finale finale(self); return; } else if (typeof then === 'function') { doResolve(then.bind(newValue), self); return; } } self._state = 1; self._value = newValue; finale(self); }
在沒有鏈式調用then
的狀況下(也就是隻要一個then
)的狀況下,會將內部狀態_state
設置成3
,將傳入值賦給內部變量_value
最後會執行final()
方法,否則則會使用doResolve
來調用then
咱們再來看下reject
function reject(self, newValue) { // _state = 2爲reject self._state = 2; self._value = newValue; if (Promise._onReject) { Promise._onReject(self, newValue); } finale(self); }
在reject
當中咱們的_state
變動爲了2,一樣最後finale
被調用。
咱們來看下finale
函數
// 執行本身的deferreds function finale(self) { if (self._deferredState === 1) { handle(self, self._deferreds); self._deferreds = null; } if (self._deferredState === 2) { for (var i = 0; i < self._deferreds.length; i++) { // 遍歷handle handle(self, self._deferreds[i]); } // 將deferred 置空 self._deferreds = null; } }
在該方法當中根據不一樣的_deferredState
,會執行不一樣的handle
方法。
咱們再來看handle
方法
function handle(self, deferred) { while (self._state === 3) { self = self._value; } // 若是有onHandle方法 則執行該方法 if (Promise._onHandle) { Promise._onHandle(self); } // (初始 _state 爲0) if (self._state === 0) { // (初始 _deferredState 爲0) if (self._deferredState === 0) { self._deferredState = 1; self._deferreds = deferred; return; } // 若是 _deferredState是1 則__deferreds是一個數組 if (self._deferredState === 1) { self._deferredState = 2; self._deferreds = [self._deferreds, deferred]; return; } // 當走到這裏 _deferredState應該是2 將deferred // 插入到數組當中 self._deferreds.push(deferred); return; } handleResolved(self, deferred); }
這裏比較關鍵的應該就是經過deferredState
不一樣的狀態,將deferred
放入deferreds
當中。另外當咱們的_state
不爲0
時,最終會執行handleResolved
。
繼續看handleResolve()
方法
function handleResolved(self, deferred) { asap(function() { // _state爲1時,cb = onFulfilled 不然 cb = onRejected var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected; if (cb === null) { if (self._state === 1) { resolve(deferred.promise, self._value); } else { reject(deferred.promise, self._value); } return; } var ret = tryCallOne(cb, self._value); if (ret === IS_ERROR) { reject(deferred.promise, LAST_ERROR); } else { resolve(deferred.promise, ret); } }); }.then((res) => { }).catch((error) => { })
在這個方法當中,會根據咱們任務(_state)的不一樣狀態,來執行onFulfilled
或者onRejected
方法。當此方法調用時,也就是咱們一個簡單的Promise
的結束。
回到剛纔說的Promise
構造方法結束的時候
設置了Promise
函數的一些變量
Promise._onHandle = null; Promise._onReject = null; Promise._noop = noop;
隨後在Promise
的原型上設置了then
方法。
Promise.prototype.then = function(onFulfilled, onRejected) { // 首先看這是誰構造的 若是不是promise // 則return 執行safeThen if (this.constructor !== Promise) { return safeThen(this, onFulfilled, onRejected); } // 若是是則初始化一個Promise 可是參數 noop 爲空對象 {} var res = new Promise(noop); // 隨後執行handle方法 handle(this, new Handler(onFulfilled, onRejected, res)); return res; };
在then
這個方法中首先判斷了它是否由Promise
構造的,若是不是,則返回並執行safeThen
,否則則執行Promise
構造一個res
對象,而後執行handle
方法,最後將promise
變量res
返回。handle
方法以前有提過,在這裏,當初始化時_state
和_deferred
的轉改都爲0
,所以它會將defrred
保存到promise
當中。
先看一下上面說的safeThen
方法
function safeThen(self, onFulfilled, onRejected) { return new self.constructor(function (resolve, reject) { var res = new Promise(noop); res.then(resolve, reject); handle(self, new Handler(onFulfilled, onRejected, res)); }); }
須要有一個Promise
的構造方法,這個構造方法最終會執行它的參數(resolve, reject) => {}
,聲明的then
方法會經過handle()
方法將onFulfilled
和onRejected
方法保存起來。當在外部調用resolve
或者onRejected
時,最終也會執行handle
可是它,會最後根據狀態來執行onFulfilled
或者onRejected
。從而到咱們的then
回調中。
對done
的擴展在src/done.js
當中
'use strict'; var Promise = require('./core.js'); module.exports = Promise; Promise.prototype.done = function (onFulfilled, onRejected) { var self = arguments.length ? this.then.apply(this, arguments) : this; self.then(null, function (err) { setTimeout(function () { throw err; }, 0); }); };
內部執行了then()
對finally
的擴展在src/finally.js
當中
在Promise
的標準當中,自己是沒有finally
方法的,可是在ES2018
的標準裏有,finally
的實現以下
'use strict'; var Promise = require('./core.js'); module.exports = Promise; Promise.prototype.finally = function (callback) { return this.then(function (value) { return Promise.resolve(callback()).then(function () { return value; }); }, function (err) { return Promise.resolve(callback()).then(function () { throw err; }); }); };
Promise
的onFulfilled
和onRejected
無論回調的哪一個,最終都會觸發callback
回調。還要注意的一點是finally
的返回也是一個Promise
。
在es6-extensions.js
文件當中包含了ES6的一些擴展。
function valuePromise(value) { var p = new Promise(Promise._noop); // 將_state賦值爲 非0 // _value進行保存 p._state = 1; p._value = value; // 這樣作的目的是省略的一些前面的邏輯 return p; } Promise.resolve = function (value) { if (value instanceof Promise) return value; if (value === null) return NULL; if (value === undefined) return UNDEFINED; if (value === true) return TRUE; if (value === false) return FALSE; if (value === 0) return ZERO; if (value === '') return EMPTYSTRING; // value return new Promise if (typeof value === 'object' || typeof value === 'function') { try { var then = value.then; if (typeof then === 'function') { // 返回 返回了一個新的Promise對象 return new Promise(then.bind(value)); } } catch (ex) { // 若是報錯 則返回一個就只 return new Promise(function (resolve, reject) { reject(ex); }); } } return valuePromise(value); };
Promise.reject = function (value) { return new Promise(function (resolve, reject) { reject(value); }); };
Promise.all = function (arr) { // 相似深拷貝了一份給了args var args = Array.prototype.slice.call(arr); return new Promise(function (resolve, reject) { // 判斷了all的promise數量 if (args.length === 0) return resolve([]); // remaining則是promise數組的長度 var remaining = args.length; // i爲index val 爲 promise function res(i, val) { if (val && (typeof val === 'object' || typeof val === 'function')) { if (val instanceof Promise && val.then === Promise.prototype.then) { while (val._state === 3) { val = val._value; } if (val._state === 1) return res(i, val._value); if (val._state === 2) reject(val._value); // val._state 爲 0時 走這裏 val.then(function (val) { res(i, val); }, reject); return; } else { var then = val.then; if (typeof then === 'function') { var p = new Promise(then.bind(val)); p.then(function (val) { res(i, val); }, reject); return; } } } args[i] = val; // 當全部的promise執行完 則是remaining爲0 // 則執行resolve(); if (--remaining === 0) { resolve(args); } } // 遍歷全部的promise for (var i = 0; i < args.length; i++) { res(i, args[i]); } }); };
Promise.all()
返回的也是一個Promise
函數。
內部有一個remaining
變量每當執行完一個promise
函數後就會減一,當全部promise
執行完,會執行本身的resolve
。
Promise.race = function (values) { return new Promise(function (resolve, reject) { values.forEach(function(value){ Promise.resolve(value).then(resolve, reject); }); }); };
遍歷傳入的promise
數組,通過Promise.resolve(value)
的源碼能夠看到,若是value
是一個Promise
則戶直接將這個value
返回,最後數組中的promise
哪一個優先回調即執行。
catch
在標準當中也是沒有,雖然咱們用的比較多
Promise.prototype['catch'] = function (onRejected) { return this.then(null, onRejected); };
catch
的回調實際是then(null, onRejected)
的回調。
本文發佈於薄荷前端週刊,歡迎Watch & Star ★,轉載請註明出處。