關於 Promise 的基礎概念和使用方法就很少講了,咱們來聊一聊它的實現原理promise
請你們配合最下方的 Promise/A+ 規範實現代碼來閱讀瀏覽器
首先明確一個概念:決議,就是指將 Promise 的狀態從 pending 變爲 fulfilled/rejected異步
其次 Promise/A+ 規範實現代碼中的 setTimeout 能夠理解將代碼添加到平臺的微任務隊列中異步執行函數
new Promise(excutor = (resolve, reject) => {}) 後 執行 excutor
, 返回一個 Promise 對象 objui
try {
excutor(resolve, reject);
} catch (e) {
reject(e);
}
複製代碼
執行 obj.then(onFulfilled, onRejected)this
首先判斷 onFulfilled 和 onRejected 是不是個函數,若是不是的話就將 onFulfilled 改寫成 value => value
,將 onRejected 改寫成 reason => { throw reason }
spa
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : value => value;
onRejected =
typeof onRejected === "function"
? onRejected
: reason => {
throw reason;
};
複製代碼
obj.then 返回一個 newPromise,它的 excutor 方法與 obj 此時的狀態有關prototype
若是 obj 爲 pending 狀態code
return newPromise = new Promise((resolve, reject) => {
that.onFulfilledCallbacks.push(value => {
try {
let x = onFulfilled(value);
resolvePromise(newPromise, x, resolve, reject);
} catch (e) {
reject(e);
}
});
that.onRejectedCallbacks.push(reason => {
try {
let x = onRejected(reason);
resolvePromise(newPromise, x, resolve, reject);
} catch (e) {
reject(e);
}
});
})
複製代碼
then 方法在 obj 處於 pending 狀態時 幹了這麼幾件事對象
建立一個新的 Promise 對象 newPromise 並返回
執行 newPromise 的 excutor,向 obj 的兩個回調隊列中添加回調
向 obj.onFulfilledCallbacks 添加一個成功回調:傳入 obj.value 執行 onFulfilled(value) 而後執行 resolvePromise,obj.value 會在 resolve(value) 執行的時候被賦值
向 obj.onRejectedCallbacks 添加一個失敗回調:傳入 obj.reason 執行 onRejected(reason) 而後執行 resolvePromise,obj.value 會在 reject(reason) 執行的時候被賦值
若是 obj 已經爲 fulfilled 狀態
return newPromise = new Promise((resolve, reject) => {
setTimeout(() => {
try {
let x = onFulfilled(that.value);
resolvePromise(newPromise, x, resolve, reject);
} catch (e) {
reject(e);
}
});
})
複製代碼
咱們一樣返回了一個新的 Promise 對象,它的 excutor 函數會將 onFulfilled 和 resolvePromise 添加進微任務隊列執行,也就是說若是 obj 在已經變爲 fulfilled 後再執行 then 方法,就會在同步任務執行完畢後當即執行 then 中傳入的 onFulfilled (這個 then 方法中的 onFulfilled 不必再加入到回調隊列中,由於它等不到 obj 執行 resolve 的那一天了,Promise 的狀態一經決議就不會改變)
若是 obj 已經爲 rejected 狀態,所作處理基本與 fulfilled 狀態下相同
下面咱們主要分析一下 pending 狀態,若是 obj 在 pending 狀態下執行了 then ,咱們就等待 excutor 中的 resolve 或者 reject 被執行
resolve 執行
接收一個參數 value , 若是 value 也是個 Promise 對象,那麼返回 value.then(reslove, reject)
// 這裏是爲了保證一個 Promise 決議時的 value 絕對不會是另外一個 Promise
// 好比 promise1 = new Promise(resolve => resolve(promise2 = new Promise())) 的狀況
// 此時 promise1 決議的時候 value 爲 promise2 ,那麼就不能繼續決議 promise1 了
// 應該執行 promise2.then(resolve, reject),等待 promise2 的決議而後再決議 promise1
if (value instanceof Promise) {
return value.then(resolve, reject);
}
複製代碼
異步執行接下來的步驟(此處的異步即放置在瀏覽器的微任務隊列中):
若是 obj.status === 'pending',改狀態爲 'fulfilled',賦值 obj.value,執行所有 onFulfilledCallbacks 回調,這個時候就會調用咱們在 then 中添加的 onFulfilled 函數
reject 執行
接收一個參數 reason,異步執行接下來的步驟:
若是 obj.status === 'pending',改狀態爲 'fulfilled',賦值 obj.reason,執行所有 onRejectedCallbacks 回調,這個時候就會調用咱們在 then 中添加的 onRejected 函數
關於異步執行
咱們說 Promise.then 是異步的,其實執行 then 方法不是異步的。可是 onFulfilled 和 onRejected 確定是異步執行的,由於 resolve 和 reject 清空回調的時候是異步執行的
執行 onFulfilled(value) 或者 onRejected(reason) 將返回值賦值給變量 x
let x = onFulfilled(value);
複製代碼
執行 resolvePromise(promise2, x, resolve, reject),根據 x 對 promise2 進行決議
resolvePromise(newPromise, x, resolve, reject);
複製代碼
注意這裏的 promise2 是 then 方法返回的 Promise 對象,resolve 和 reject 是 promise2 的 excutor 中的 resolve 和 reject,也就是說 resolvePromise 的執行過程當中會對 promise2 進行決議
咱們仔細分析一下 resolvePromise 都作了什麼
首先這個參數 x 就是 onFulfilled(value) 的返回值,若是 onFulfilled 不是個函數,那它就會被改寫成 v => v
,也就是 x = value
接下來就是判斷 x 究竟是什麼
若是 x === promise2,報循環引用的錯誤
若是 x 是一個 Promise 對象且狀態爲 pending,那麼就執行 x.then 將 onFulfilled 加入 x.onFulfilledCallbacks 回調並等待 x 決議, x 決議後執行回調中的 onFulfilled(x.value) 也就是 x.value => resolvePromise(promise2, x.value, resolve, reject)
,而上文提到過此時的 x.value 毫不多是一個 Promise 了
x.then(
y => {
resolvePromise(promise2, y, resolve, reject);
},
reason => {
reject(reason);
}
);
複製代碼
若是 x 是一個 thenable 對象,處理方式同 pending 狀態的 Promise 對象,這裏使用一個 called 鎖來避免 thenable.then 屢次調用傳入的 onFulfilled/onRejected
若是 x 是一個 Promise 對象且狀態不爲 pending,因此 x.value 或者 x.reason 必會存在一個,此時執行 x.then(resolve, reject)
,直接決議 promise2 ,即執行 resolve(x.value) 或者 reject(x.reason)
若是 x 是一個普通的函數/對象,或者其餘數據類型,則 resolve(x)
promise1 = new Promise(excutor = (resolve, reject) => { ... }) 中的 excutor 是當即執行的,但最後執行 resolve 多是在異步操做中
promise1.then 會給 promise1 添加回調,而後返回一個新的 promise2,這個新的 promise2 的決議依靠以前回調中的 resolvePromise 方法
promise1 決議後會執行回調,首先執行 then 中傳入的 onFulfilled(promise1.value),賦值給變量 x,再執行 resolvePromise(promise2, x, promise2Resolve, promise2Reject)
若是 x 是個已決議的 Promise 或者普通的數據類型,那麼就能夠 promise2Resolve(x) 決議 promise2
若是 x 是個 pending 狀態的 promise 或者 thenable 對象,那麼執行 x.then ,將 resolvePromise 放入 x 的成功回調隊列,等待 x 決議後將 x.value 成功賦值,而後執行 resolvePromise(promise2, x.value, promise2Resolve, promise2Reject)
在此期間若是執行了 promise2.then 就新建一個 promise3 並返回 ,將新傳入的 onFulfilled(promise2.value) 和針對 promise3 的 resolvePromise 傳入 promise2 的成功回調隊列中,等待 promise2 的決議
promise3.then 同上,就此實現了鏈式調用
promise1 決議後纔會決議 promise2 ,由於 promise2 的決議方法要在 promise1 的成功回調裏執行
若是 promise1.then 中傳入的的 onFulfilled 不是個函數,那麼 onFulfilled 會在 then 中被改寫成 value => value
,這樣就能夠將 promise1.value 傳遞給 promise2 的 resolvePromise 幫助它決議。若是剛好 value 不是一個 pending 狀態的 Promise 或者 thenable 對象,那麼 promise2 會直接決議,而後 promise1.value 會被賦值給 promise2.value 進而傳遞給 promise3,這就是所謂的透傳
若是 promise2.then 的 onFulfilled 反回了一個新的 PromiseA,那麼 promise2 將直接取這個 PromiseA 的狀態和值爲己用,這發生在 resolvePromise 中;
若是 PromiseA 的 resolve 時傳入的 value 是一個新的 PromiseB,那麼 PromiseA 也會直接取這個 PromiseB 的狀態和值爲己用,這發生在 resolve 中;
也就是說,咱們能夠經過在 onFulfilled 時返回一個 newPromise,或者 resolve 時傳入一個 newPromise,再執行 newPromise.then(resolve, reject)
來實現一個 Promise 決議的掛起(後置),直至 newPromise 決議。
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
function Promise(excutor) {
let that = this;
that.status = PENDING;
that.value = undefined;
that.reason = undefined;
that.onFulfilledCallbacks = [];
that.onRejectedCallbacks = [];
function resolve(value) {
if (value instanceof Promise) {
return value.then(resolve, reject);
}
setTimeout(() => {
if (that.status === PENDING) {
that.status = FULFILLED;
that.value = value;
that.onFulfilledCallbacks.forEach(cb => cb(that.value));
}
});
}
function reject(reason) {
setTimeout(() => {
if (that.status === PENDING) {
that.status = REJECTED;
that.reason = reason;
that.onRejectedCallbacks.forEach(cb => cb(that.reason));
}
});
}
try {
excutor(resolve, reject);
} catch (e) {
reject(e);
}
}
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
return reject(new TypeError("Chaining cycle detected for promise!"));
}
let called = false;
if (x instanceof Promise) {
if (x.status === PENDING) {
x.then(
y => {
resolvePromise(promise2, y, resolve, reject);
},
reason => {
reject(reason);
}
);
} else {
x.then(resolve, reject);
}
} else if (x != null && (typeof x === "object" || typeof x === "function")) {
try {
let then = x.then;
if (typeof then === "function") {
then.call(
x,
y => {
if (called) return;
called = true;
resolvePromise(promise2, y, resolve, reject);
},
reason => {
if (called) return;
called = true;
reject(reason);
}
);
} else {
resolve(x);
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else {
resolve(x);
}
}
Promise.prototype.then = function(onFulfilled, onRejected) {
const that = this;
let newPromise;
onFulfilled =
typeof onFulfilled === "function" ? onFulfilled : value => value;
onRejected =
typeof onRejected === "function"
? onRejected
: reason => {
throw reason;
};
if (that.status === FULFILLED) {
return (newPromise = new Promise((resolve, reject) => {
setTimeout(() => {
try {
let x = onFulfilled(that.value);
resolvePromise(newPromise, x, resolve, reject);
} catch (e) {
reject(e);
}
});
}));
}
if (that.status === REJECTED) {
return (newPromise = new Promise((resolve, reject) => {
setTimeout(() => {
try {
let x = onRejected(that.reason);
resolvePromise(newPromise, x, resolve, reject);
} catch (e) {
reject(e);
}
});
}));
}
if (that.status === PENDING) {
return (newPromise = new Promise((resolve, reject) => {
that.onFulfilledCallbacks.push(value => {
try {
let x = onFulfilled(value);
resolvePromise(newPromise, x, resolve, reject);
} catch (e) {
reject(e);
}
});
that.onRejectedCallbacks.push(reason => {
try {
let x = onRejected(reason);
resolvePromise(newPromise, x, resolve, reject);
} catch (e) {
reject(e);
}
});
}));
}
};
複製代碼