前言
上一次我們寫完了Promise的基礎功能,明白了promise為何能在任務成功的時候,調成功的回撥函式,為何在任務失敗的時候調失敗的回撥函式.今天就把最麻煩的promise鏈式呼叫給搞定吧.
附上上篇文章的連結:最最最通俗易懂的promise手寫系列(一)
再附上上次的程式碼吧,以免翻來翻去麻煩.
function Promise(executor) {
let self = this;
self.value = undefined;
self.reason = undefined;
self.status = `pending`;
self.onFulFilledCallbacks = [];
self.onRejectedCallbacks = [];
function resolve(value) {
if (self.status === `pending`) {
self.value = value;
self.status = `resolved`
self.onFulFilledCallbacks.forEach(onFulFilled => {
onFulFilled(self.value)
});
}
}
function reject(reason) {
if (self.status === `pending`) {
self.reason = reason;
self.status = `rejected`;
self.onRejectedCallbacks.forEach(onRejected => {
onRejected(self.reason)
});
}
}
try {
executor(resolve, reject);
} catch (error) {
reject(error)
}
}
Promise.prototype.then = function (onFulFilled, onRejected) {
if (this.status === `pending`) {
this.onFulFilledCallbacks.push(() => {
onFulFilled(this.value)
});
this.onRejectedCallbacks.push(() => {
onRejected(this.reason)
})
} else if (this.status === `resolved`) {
onFulFilled(this.value);
} else if (this.status === `rejected`) {
onRejected(this.reason);
}
}
複製程式碼
我們要完成的目標:
let p = new Promise(function (resove, reject) {
setTimeout(() => {
console.log(`任務執行完了`);
resove()
},1500)
});
p.then(function (value) {
console.log(`第一個成功回撥`)
},function () {})
.then(function () {
console.log(`第二個成功回撥`)
}, function () {});
輸出如下:
任務執行完了
第一個成功回撥
第二個成功回撥
複製程式碼
- 我們都熟悉Jquery,它的鏈式呼叫是用
rerun this
來做的,可是這裡卻不行,原因文章末尾再解釋。
我們採取返回一個新的promise物件來實現鏈式呼叫. - 意思也就是p.then()返回一個新promise物件.
//給這個函式加個返回值,返回值就是一個新new的promise物件
Promise.prototype.then = function (onFulFilled, onRejected) {
let p2 = new Promise((resolve, reject) => {});
if (this.status === `pending`) {
this.onFulFilledCallbacks.push(() => {
onFulFilled(this.value)
});
this.onRejectedCallbacks.push(() => {
onRejected(this.reason)
})
} else if (this.status === `resolved`) {
onFulFilled(this.value);
} else if (this.status === `rejected`) {
onRejected(this.reason);
}
return p2;
}
複製程式碼
- 加了兩行程式碼,我們測試一下輸出結果:
任務執行完了
第一個成功回撥 - 根據上一次我們所講的可以總結出如下結論:真正觸發onFulFilledCallbacks裡所儲存的回撥函式只有兩個地方:
- resolve被呼叫的時候.(resolve是使用者呼叫的,因為使用者當然知道哪種邏輯算任務成功,哪種邏輯算任務失敗,例如我們上一章的例子,隨機數大於5我就認為是成功的.)
- then被呼叫的時候.(then也是使用者呼叫的,promise裡的任務執行完了後要做啥)
這裡我們理一下流程:在我們new Promise的時候,executor就同步的執行了,根據executor裡有無非同步操作分一下兩種情況:
1.有非同步操作,如我們的例子裡,有setTimout,延時1.5s後列印。
- executor函式執行完,p也就完成了new的過程。(此時還不會列印輸出
任務執行完了
,因為setTimout是非同步的) - 拿到p物件後,程式碼接著執行p.then,此時很明顯狀態是pending,我們會把使用者在then裡傳的回撥函式存起來(onFulFilledCallbacks),在1.5s後,setTimeout裡呼叫resovle的時候,會遍歷onFulFilledCallbacks,執行之前then裡傳的函式
- 這也就是我們之前為什麼要給promise加三個狀態的原因(等待pending 成功resolve 失敗rejected),害怕裡面有非同步任務。
不管如何非同步,使用者總是知道程式碼走到哪兒算是成功了(在成功的地方調resolve),程式碼走到哪兒算是出異常了(在失敗的地方調reject)這是規矩,使用Promise必須遵守的規矩。
2.沒有非同步操作,如我們的例子裡,有setTimout,延時1.5s後列印。
- 這個就簡單了,跟著程式碼順序看就行了,執行executor直接就會調resolve,then的會後,任務已經完成,當即執行使用者傳的回撥函式就行了
回到我們要解決的問題剛只是輸出了第一個成功回撥,因為【p2】的status是pending呀(我們在內部自己new的Promise,內部沒有任何要執行的東西,沒有調resolve,那我們就調一下唄).
//給這個函式加個返回值,返回值就是一個新new的promise物件
Promise.prototype.then = function (onFulFilled, onRejected) {
let p2 = new Promise((resolve, reject) => { resove() });
if (this.status === `pending`) {
this.onFulFilledCallbacks.push(() => {
onFulFilled(this.value)
});
this.onRejectedCallbacks.push(() => {
onRejected(this.reason)
})
} else if (this.status === `resolved`) {
onFulFilled(this.value);
} else if (this.status === `rejected`) {
onRejected(this.reason);
}
return p2;
}
複製程式碼
- 我們測試一下輸出結果:
第二個成功回撥
任務執行完了
第一個成功回撥 - 這一會倒是執行了,可是順序亂了,原因:p.then拿到p2,p2立刻就resolve了,當程式碼走到p2.then自然就是直接走到this.status === `resolved`,執行傳入的函式了.等1.5s後第一個promsie任務完成,列印
任務執行完了
呼叫第一個的resolve,第一個成功回撥
- 我們想要的是自己控制順序,看直白一點就是我們得控制在第一個Promise也就是P任務執行完了,才能調P2.resove,那就直接在第一個Promise回撥函式執行完了我們再調P2.resove吧
//給這個函式加個返回值,返回值就是一個新new的promise物件
Promise.prototype.then = function (onFulFilled, onRejected) {
//p2的resolve在裡面,外面拿不到,只有這樣很賤的給在外面記下來了
let p2Resolve ;
let p2Reject;
let p2 = new Promise((resolve, reject) => {
p2Resolve = resolve;
p2Reject = reject;
});
if (this.status === `pending`) {
this.onFulFilledCallbacks.push(() => {
onFulFilled(this.value)
p2Resolve()
});
this.onRejectedCallbacks.push(() => {
onRejected(this.reason)
p2Reject()
})
} else if (this.status === `resolved`) {
onFulFilled(this.value);
p2Resolve()
} else if (this.status === `rejected`) {
onRejected(this.reason);
p2Reject()
}
return p2;
}
複製程式碼
輸出如下:
任務執行完了
第一個成功回撥
第二個成功回撥
結語
我們剩下onFulFilled返回值的功能沒做,下次子再來.
感謝各位觀眾.