本文首發於 vivo互聯網技術 微信公衆號
連接: https://mp.weixin.qq.com/s/Lp_5BXdpm7G29Z7zT_S-bQ
做者:Morraingit
了用法,原生提供了Promise對象。更多關於 Promise 的介紹請參考阮一峯老師的 ES6入門 之 Promise 對象。es6
不少同窗在學習 Promise 時,知其然殊不知其因此然,對其中的用法理解不了。本系列文章由淺入深逐步實現 Promise,並結合流程圖、實例以及動畫進行演示,達到深入理解 Promise 用法的目的。github
本系列文章有以下幾個章節組成:設計模式
圖解 Promise 實現原理(四)—— Promise 靜態方法實現bash
上一節中,實現了 Promise 的原型方法。包括增長異常狀態,catch以及 finally。截至目前,Promise 的實現以下:微信
class Promise {
callbacks = [];
state = 'pending';//增長狀態
value = null;//保存結果
constructor(fn) {
fn(this._resolve.bind(this), this._reject.bind(this));
}
then(onFulfilled, onRejected) {
return new Promise((resolve, reject) => {
this._handle({
onFulfilled: onFulfilled || null,
onRejected: onRejected || null,
resolve: resolve,
reject: reject
});
});
}
catch(onError) {
return this.then(null, onError);
}
finally(onDone) {
if (typeof onDone !== 'function') return this.then();
let Promise = this.constructor;
return this.then(
value => Promise.resolve(onDone()).then(() => value),
reason => Promise.resolve(onDone()).then(() => { throw reason })
);
}
_handle(callback) {
if (this.state === 'pending') {
this.callbacks.push(callback);
return;
}
let cb = this.state === 'fulfilled' ? callback.onFulfilled : callback.onRejected;
if (!cb) {//若是then中沒有傳遞任何東西
cb = this.state === 'fulfilled' ? callback.resolve : callback.reject;
cb(this.value);
return;
}
let ret;
try {
ret = cb(this.value);
cb = this.state === 'fulfilled' ? callback.resolve : callback.reject;
} catch (error) {
ret = error;
cb = callback.reject
} finally {
cb(ret);
}
}
_resolve(value) {
if (value && (typeof value === 'object' || typeof value === 'function')) {
var then = value.then;
if (typeof then === 'function') {
then.call(value, this._resolve.bind(this), this._reject.bind(this));
return;
}
}
this.state = 'fulfilled';//改變狀態
this.value = value;//保存結果
this.callbacks.forEach(callback => this._handle(callback));
}
_reject(error) {
this.state = 'rejected';
this.value = error;
this.callbacks.forEach(callback => this._handle(callback));
}
}複製代碼
除了前文中提到的 Promise實例的原型方法外,Promise 還提供了 Promise.resolve 和Promise.reject 方法。用於將非 Promise 實例包裝爲 Promise 實例。例如:異步
Promise.resolve('foo')
// 等價於
new Promise(resolve => resolve('foo'))複製代碼
const Id2NameMap = {};
const getNameById = function (id) {
if (Id2NameMap[id]) return Id2NameMap[id];
return new Promise(resolve => {
mockGetNameById(id, function (name) {
Id2NameMap[id] = name;
resolve(name);
})
});
}
getNameById(id).then(name => {
console.log(name);
});複製代碼
其實上面的代碼是有問題的,若是命中 Id2NameMap 裏的值,getNameById 返回的結果就是 name,而不是 Promise 實例。此時 getNameById(id).then 會報錯。在咱們不清楚返回的是不是 Promise 實例的狀況下,就可使用 Promise.resolve 進行包裝:
Promise.resolve(getNameById(id)).then(name => {
console.log(name);
});複製代碼
在實現 Promise.resolve 以前,咱們先看下它的參數分爲哪些狀況:
(1)參數是一個 Promise 實例
若是參數是 Promise 實例,那麼 Promise.resolve 將不作任何修改、原封不動地返回這個實例。
(2)參數是一個 thenable 對象
thenable 對象指的是具備 then 方法的對象,好比下面這個對象。
let thenable = {
then: function(onFulfilled) {
onFulfilled(42);
}
};複製代碼
let thenable = {
then: function(onFulfilled) {
onFulfilled(42);
}
};
let p1 = Promise.resolve(thenable);
p1.then(function(value) {
console.log(value); // 42
});複製代碼
(3)參數不是具備 then 方法的對象,或根本就不是對象
若是參數是一個原始值,或者是一個不具備then方法的對象,則 Promise.resolve 方法返回一個新的 Promise 對象,狀態爲 resolved。
(4)不帶任務參數
Promise.resolve 方法容許調用時不帶參數,直接返回一個 resolved 狀態的 Promise 對象。
static resolve(value) {
if (value && value instanceof Promise) {
return value;
} else if (value && typeof value === 'object' && typeof value.then === 'function') {
let then = value.then;
return new Promise(resolve => {
then(resolve);
});
} else if (value) {
return new Promise(resolve => resolve(value));
} else {
return new Promise(resolve => resolve());
}
}複製代碼
Promise.all 接收一個 Promise 實例的數組,在全部這些 Promise 的實例都 fulfilled 後,按照 Promise 實例的順序返回相應結果的數組。
const p1 = new Promise((resolve, reject) => {
setTimeout(() => resolve('p1'), 1000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => resolve('p2'), 5000)
})
Promise.all([p1, p2]).then(rets => {
console.log(rets) // ['p1','p2']
})複製代碼
static all(promises) {
return new Promise((resolve, reject) => {
let fulfilledCount = 0
const itemNum = promises.length
const rets = Array.from({ length: itemNum })
promises.forEach((promise, index) => {
Promise.resolve(promise).then(result => {
fulfilledCount++;
rets[index] = result;
if (fulfilledCount === itemNum) {
resolve(rets);
}
}, reason => reject(reason));
})
})
}複製代碼
Promise.race 也接收一個 Promise 實例的數組,與 Promise.all不一樣的是,因此返回的結果是這些 Promise 實例中最早 fulfilled 的。
const p1 = new Promise((resolve, reject) => {
setTimeout(() => resolve('p1'), 1000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => resolve('p2'), 5000)
})
Promise.race([p1, p2]).then(ret => {
console.log(ret) // 'p1'
})複製代碼
static race(promises) {
return new Promise(function (resolve, reject) {
for (let i = 0; i < promises.length; i++) {
Promise.resolve(promises[i]).then(function (value) {
return resolve(value)
}, function (reason) {
return reject(reason)
})
}
})
}複製代碼
剛開始看 Promise 源碼的時候總不能很好的理解 then 和 resolve 函數的運行機理,可是若是你靜下心來,反過來根據執行 Promise 時的邏輯來推演,就不難理解了。這裏必定要注意的點是:Promise 裏面的 then 函數僅僅是註冊了後續須要執行的代碼,真正的執行是在 resolve 方法裏面執行的,理清了這層,再來分析源碼會省力的多。
如今回顧下 Promise 的實現過程,其主要使用了設計模式中的觀察者模式:
經過 Promise.prototype.then 和 Promise.prototype.catch 方法將觀察者方法註冊到被觀察者 Promise 對象中,同時返回一個新的 Promise 對象,以即可以鏈式調用。
被觀察者管理內部 pending、fulfilled 和 rejected 的狀態轉變,同時經過構造函數中傳遞的 resolve 和 reject 方法以主動觸發狀態轉變和通知觀察者。
本系列圖文講解的是 Promise 的思想,實現的內容並不能徹底知足 Promise/A+ 規範的全部要求。
更多內容敬請關注 vivo 互聯網技術 微信公衆號
注:轉載文章請先與微信號:Labs2020 聯繫。