說到Promise,我們首先想到的最核心的功能就是異步鏈式調用,本篇文章就帶你用20行代碼實現一個可以異步鏈式調用的Promise。
這個Promise的實現不考慮任何異常情況,只考慮代碼最簡短,從而便於讀者理解核心的異步鏈式調用原理。
代碼
先給代碼吧,真就20行。
<code>function Promise(excutor) {
var self = this
self.onResolvedCallback = []
function resolve(value) {
setTimeout(() => {
self.data = value
self.onResolvedCallback.forEach(callback => callback(value))
})
}
excutor(resolve.bind(self))
}
Promise.prototype.then = function(onResolved) {
var self = this
returnnewPromise(resolve => {
self.onResolvedCallback.push(function() {
var result = onResolved(self.data)
if (result instanceofPromise) {
result.then(resolve)
} else {
resolve(result)
}
})
})
}/<code>
核心案例
<code>newPromise(resolve => {
setTimeout(() => {
resolve(1)
}, 500)
})
.then(res => {
console.log(res)
returnnewPromise(resolve => {
setTimeout(() => {
resolve(2)
}, 500)
})
})
.then(console.log)/<code>
本文將圍繞這個最核心的案例來講,這段代碼的表現如下:
- 500ms後輸出1
- 500ms後輸出2
實現
構造函數
首先來實現Promise構造函數
<code>function Promise(excutor) {
var self = this
self.onResolvedCallback = [] // Promise resolve時的回調函數集
// 傳遞給Promise處理函數的resolve
// 這裡直接往實例上掛個data
// 然後把onResolvedCallback數組裡的函數依次執行一遍就可以
function resolve(value) {
// 注意promise的then函數需要異步執行
setTimeout(() => {
self.data = value
self.onResolvedCallback.forEach(callback => callback(value))
})
}
// 執行用戶傳入的函數
excutor(resolve.bind(self))
}/<code>
好,寫到這裡先回過頭來看案例
<code>const excutor = resolve => {
setTimeout(() => {
resolve(1)
}, 500)
}
newPromise(excutor)/<code>
分開來看,excutor就是用戶傳的函數,這個函數內部調用了resolve函數後,就會把promise實例上的onResolvedCallback執行一遍。
到此為止我們還不知道onResolvedCallback這個數組裡的函數是從哪裡來的,接著往下看。
then
這裡是最重要的then實現,鏈式調用全靠它:
<code>Promise.prototype.then = function(onResolved) {
// 保存上下文,哪個promise調用的then,就指向哪個promise。
var self = this
// 一定要返回一個新的promise
// promise2
returnnewPromise(resolve => {
self.onResolvedCallback.push(function() {
var result = onResolved(self.data)
if (result instanceofPromise) {
// resolve的權力被交給了user promise
result.then(resolve)
} else {
resolve(result)
}
})
})
}/<code>
再回到案例裡
<code>var excutor = resolve => {
setTimeout(() => {
resolve(1)
}, 500)
}
var promise1 = newPromise(excutor)
promise1.then(res => {
console.log(res)
// user promise
returnnewPromise(resolve => {
setTimeout(() => {
resolve(2)
}, 500)
})
})/<code>
注意這裡的命名:
- 我們把Promise構造函數返回的實例叫做promise1,
- 在then的實現中,我們構造了一個新的promise返回,叫它promise2
- 在用戶調用then方法的時候,用戶手動構造了一個promise用來做異步的操作,叫它user promise
那麼在then的實現中,self其實就指向promise1
而promise2的excutor中,立刻執行了一個函數,它往promise1的onResolvedCallback數組中push了一個函數,
那麼重點看這個push的函數,注意,這個函數在promise1被resolve了以後才會執行。
<code>self.onResolvedCallback.push(function() {
// onResolved就對應then傳入的函數
var result = onResolved(self.data)
// 例子中的情況 返回了一個promise3
if (result instanceofPromise) {
// 那麼直接把promise2的resolve決定權交給了user promise
result.then(resolve)
} else {
resolve(result)
}
})/<code>
如果用戶傳入給then的onResolved方法返回的是個promise,那麼這個user promise裡拿到的參數resolve,其實就指向了內部promise2的resolve,
所以這就可以做到:user promise被resolve以後,then2函數才會繼續執行,
<code>newPromise(resolve => {
setTimeout(() => {
// resolve1
resolve(1)
}, 500)
})
// then1
.then(res => {
console.log(res)
// user promise
returnnewPromise(resolve => {
setTimeout(() => {
// resolve2
resolve(2)
}, 500)
})
})
// then2
.then(console.log)/<code>
then1其實進入了promise1的回調數組裡,所以resolve1執行完畢後,then1才會執行
then2其實進入了promise2的回調數組裡,又因為我們剛剛知道,resolve2正是promise2的resolve方法,
所以resolve2執行完畢後, then2才會執行,這就實現了異步的鏈式調用。
要點總結
一個核心的要點:
- 簡單情況 then1函數是個同步函數,返回一個普通的值。then1裡傳入的函數,其實是被放到promise1的回調數組裡,
<code>// promise1
newPromise(resolve => {
setTimeout(resolve, 1000)
})
// then1 這裡傳入的函數 會被放到調用者promise的回調數組中
.then(res => {
console.log(res)
})/<code>
這樣的話,1秒後,promise1被resolve了,是不是then1裡的函數就被執行了呢~
- 複雜情況 then函數返回了個promise 如果這個then函數里返回了一個promise,那麼這個返回的promise內部的resolve,其實就指向
<code>// 調用then的promise
newPromise(resolve => {
setTimeout(resolve, 1000)
})
// then2
.then(res => {
// user promise
returnnewPromise(resolve => {
setTimeout(resolve, 1000)
})
})
// then3
.then(res => {
console.log(res)
})/<code>
then2會返回promise2(注意不是user promise,而是源碼內部返回的那個promise2),
then3傳入的函數會被放到promise2的回調數組裡。
由於then2中用戶自己返回了一個user promise,
所以promise2的resolve權力會被交給user promise,
在1秒後,user promise被resolve了,那麼代表著promise2被reoslve了,那麼在promise2的回調數組裡會找到then3傳入的回調函數
它就被完美的執行了。
閱讀更多 流浪的思維 的文章
關鍵字: promise2 Promise setTimeout