系列文章
- Promise源碼解密-PromisesA+标準
- Promise源碼解密-同步版
- Promise源碼解密-異步版
- Promise源碼解密-then的鍊式調用
- Promise源碼解密-catch/resolve/reject/race
創作不易 拒絕白嫖 點個贊呗
關注專欄
Promise源碼解密 ,帶你走進Promise的深處!!!
術語
1. Promise
promise 是一個擁有
then
方法的 object 或 function ,其行為符合本規範
2. thenable
是一個定義了
then
方法的 object 或 function
3. 值(value)
指任何 JavaScript 的合法值(包括
undefined
, thenable 和 promise);
4. 異常(exception)
通過throw抛出一個值
5. 原因(reason)
一個promise被拒絕的原因
class Promise {
constructor(executor) {
this.value = undefined;
this.reason = undefined;
}
}
function Promise(){}
Promise.prototype.then=function(){}
要求
1. Promise 的狀态
目前Promise的狀态隻能是下面三種狀态中的一種,不能存在多種狀态。
示例:
const STATUS_PENDING = "pending";
const STATUS_FULFILLED = "fulfilled";
const STATUS_REJECTED = "rejected";
class Promise {
constructor(executor) {
this.status = STATUS_PENDING;
}
}
1.1. 等待(Pending)
處于等待時,狀态可以遷移至執行态或拒絕态
1.2. 執行(Fulfilled)
處于執行時:
- 不能遷移至其他任何狀态
- 必須擁有一個不可變的終值
1.3. 拒絕(Rejected)
處于拒絕時:
- 必須擁有一個不可變的reason
這裡的不可變指的是恒等(即可用
===
判斷相等),引用資料類型隻要保證位址相等即可。
new Promise((resolve, reject) => {
resolve('成功');
reject('拒絕')
}).then((res) => {
console.log(res,"res")
}, (error) => {
console.log(error,'reason')
})
new Promise()時狀态是pending,
當程式執行,也就是(resolve, reject) => {
resolve('成功');
reject('拒絕')
},這個執行
這裡resolve把值進行了this.value = "成功";,但是并不會this.reason='拒絕',
這是因為隻有出現了執行就不會出現拒絕,最後輸出的結果是"成功 res",
如果注釋掉resolve('成功');,最後輸出的結果是 "拒絕 reason"。
2. Then 方法
一個 promise 必須提供一個
then
方法以通路其目前值、終值 和 reason。
promise 的
then
方法接受兩個函數參數:
promise.then(onFulfilled, onRejected)
then((res) => {
console.log(res,"res")
}, (error) => {
console.log(error,'reason')
})
2.1 onFulfilled
和 onRejected
都是可選參數。
onFulfilled
onRejected
- 如果
不是函數,必須被忽略onFulfilled
-
onRejected
2.2 onFulfilled
onFulfilled
onFulfilled
是函數:
- 當
執行結束後其必須被調用,其第一個參數為promise
的valuepromise
- 在
執行結束前其不可被調用promise
- 其調用次數不可超過一次
new Promise((resolve, reject) => {
resolve(123)
}).then(result => {
console.log(result);
}, error => {
console.log(error);
});
// 這裡resolve執行結束才會執行then,這裡的result就是終值
2.3 onRejected
onRejected
-
被拒絕執行後其必須被調用,其第一個參數為promise
的reasonpromise
-
被拒絕執行前其不可被調用promise
2.4 調用時機
onFulfilled
onRejected
隻有在
執行環境堆棧僅包含平台代碼時才可被調用 >> 注1
2.5 調用要求
onFulfilled
onRejected
必須被作為函數調用(即沒有
this
值)>> 注2
2.6 多次調用
then
方法可以被同一個
promise 調用多次
-
成功執行時,所有promise
需按照其注冊順序依次回調onFulfilled
-
被拒絕執行時,所有的promise
onRejected
new Promise.then().then()
2.7 傳回
then
方法必須傳回一個
promise
對象 >> 注3, 這也就是then可以被多次調用的原因。
Promise.prototype.then=function(onFulfilled, onRejected){
return new Promise()
}
promise2 = promise1.then(onFulfilled, onRejected);
-
或者onFulfilled
傳回一個值onRejected
,則運作下面的 Promise 解決過程:x
[[Resolve]](promise2, x)
promise1.then((value)=>{
return x;
}, (reason)=>{
return x
}),
// 這種情況需要處理x函數還是值還是其他情況 ,所有用到了 Promise 解決過程
// 這種情況直接
Promise.prototype.then=function(onFulfilled, onRejected){
return let promise2 = new Promise((resolve,reject)=>{
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
}
-
onFulfilled
抛出一個異常onRejected
,則e
必須拒絕執行,并傳回拒因promise2
e
promise1.then((value)=>{
throw new Error()
}, (reason)=>{
throw new Error()
}),
// 這種情況直接
Promise.prototype.then=function(onFulfilled, onRejected){
return new Promise((resolve,reject)=>{
try {
// console.log("執行 onFulfilled");
// 這裡的x是啥? 是then中回調的return的傳回值
onFulfilled(this.value);
或者
onReject(this.reason)
} catch (e) {
reject(e);
}
})
}
-
不是函數且onFulfilled
成功執行,promise1
必須成功執行并傳回相同的值promise2
promise1.then(1,2),
Promise.prototype.then=function(onFulfilled, onRejected){
if (this.status === STATUS_FULFILLED) {
return new Promise((resolve,reject)=>{
if(typeof onFulfilled !== "function" ){
resolve(onFulfilled)
}
if(typeof onRejected !== "function" ){
resolve(onRejected)
}
})
}
}
-
onRejected
拒絕執行,promise1
必須拒絕執行并傳回相同的reasonpromise2
promise1.then(1,2),
Promise.prototype.then=function(onFulfilled, onRejected){
if (this.status === STATUS_PENDING) {
return new Promise((resolve,reject)=>{
reject(onRejected)
})
}
}
Promise 解決過程
Promise 解決過程是需要輸入一個 promise 和一個值 x 的一個抽象的操作,稱為
[[Resolve]](promise, x)
,如果
x
有
then
方法且看上去像一個 Promise ,解決程式即嘗試使
promise
接受
x
的狀态;否則其用
x
的值來執行
promise
。(x是傳回值,請注意上面的例子)
解決過程步驟:
x
與 promise
相等
x
promise
promise
x
指向同一對象,直接 throw new TypeError
let p1=new Promise(resolve=>{
resolve()
})
let p2=p1.then(data=>{
// 傳回了p2
return p2
})
x
是Promise
x
x
是一個 Promise ,則使
promise
x
的狀态 >>注4:
-
處于等待,x
需保持等待直至promise
被執行或拒絕x
-
處于執行,用相同的值執行x
promise
-
處于拒絕,用相同的reason拒絕x
promise
x
為對象或函數
x
x
為對象或者函數:
- 把
指派給x.then
>> 注5then
- 如果取
的值時抛出錯誤x.then
,則以e
為reason拒絕e
promise
- 如果then是函數,将x作為函數的作用域this調用。傳遞兩個回調函數作為參數,第一個參數叫做resolvePromise,第二個參數叫做rejectPromise:
-
-
以值resolvePromise
為參數被調用,則運作y
[[Resolve]](promise, y)
-
以reasonrejectPromise
為參數被調用,則以reasonr
拒絕r
promise
-
resolvePromise
均被調用,或者被同一參數調用了多次,則優先采用首次調用并忽略剩下的調用rejectPromise
- 如果調用then方法抛出了異常e:
-
-
-
-
或resolvePromise
已經被調用,則忽略之rejectPromise
-
-
- 否則以
e
promise
-
-
不是函數,以then
為參數執行x
promise
-
-
不為對象或者函數,以x
x
promise
function resolvePromise(promise2, x, resolve, reject) {
// 用來儲存是否已經reject或者resolve過
let called
if (promise2 === x) {
throw new TypeError('Chaining cycle detected for promise')
}
// 如果是函數或者object的話先預設是promise
if (x != null && (typeof x === 'object' || typeof x === 'function')) {
try {
let then = x.then
// 如果then是函數的話
if (typeof then === 'function') {
// 為啥不直接x.then()
// 因為then已經判斷過是不是function,但是x.then沒有判斷過
// 就讓then執行 第一個參數是this 後面是成功的回調 和 失敗的回調
// 這裡的y是啥,如果x是promsie的話,那麼y就是x中的resolve/reject的值
then.call(x, y => {
// 成功和失敗隻能調用一個
if (called) return;
called = true;
// resolve的結果依舊是promise 那就繼續解析
resolvePromise(promise2, y, resolve, reject);
}, err => {
// 成功和失敗隻能調用一個
if (called) return;
called = true;
reject(err);
})
} else {
resolve(x); // 如果不是函數,那就直接傳回結果
}
} catch (error) {
// 成功和失敗隻能調用一個
if (called) return;
called = true;
// 沒有then 不是函數也不是普通值
reject(error)
}
} else {
// x 是一個普通值
resolve(x)
}
}
如果一個 promise 被一個循環的 thenable 鍊中的對象解決,而
[[Resolve]](promise, thenable)
的遞歸性質又使得其被再次調用,根據上述的算法将會陷入無限遞歸之中。算法雖不強制要求,但也鼓勵施者檢測這樣的遞歸是否存在,若檢測到存在則以一個可識别的
TypeError
為reason來拒絕
promise
>>注6。
注釋
- 注1 這裡的平台代碼指的是引擎、環境以及 promise 的實施代碼。實踐中要確定
onFulfilled
方法異步執行,且應該在onRejected
方法被調用的那一輪事件循環之後的新執行棧中執行。這個事件隊列可以采用“宏任務(macro-task)”機制或者“微任務(micro-task)”機制來實作。由于 promise 的實施代碼本身就是平台代碼(**譯者注:**即都是 JavaScript),故代碼自身在處理在處理程式時可能已經包含一個任務排程隊列。then
- 注2 也就是說在**嚴格模式(strict)**中,函數
的值為this
;在非嚴格模式中其為全局對象。undefined
- 注3 代碼實作在滿足所有要求的情況下可以允許
。每個實作都要文檔說明其是否允許以及在何種條件下允許promise2 === promise1
。promise2 === promise1
- 注4 總體來說,如果
符合目前實作,我們才認為它是真正的 promise 。這一規則允許那些特例實作接受符合已知要求的 Promises 狀态。x
- 注5 這裡我們存儲了x.then的引用 ,然後在if中進行判斷,并且可以避免多次通路
屬性。同時這確定了該屬性的一緻性,因為其值可能在檢索調用時被改變。x.then
- 注6 實作不應該對 thenable 鍊的深度設限,并假定超出本限制的遞歸就是無限循環。隻有真正的循環遞歸才應能導緻
異常;如果一條無限長的鍊上 thenable 均不相同,那麼遞歸下去永遠是正确的行為。TypeError