實現自己的Promise(1)
一直以來都對Promise的實現有濃厚的興趣、感覺很好玩。很想搞清楚他的原理是什麼,而最好的辦法莫過於寫一個自己的Promise了。 首先我們需要看一下Promise的基本使用方式是什麼樣子的。
let testPromise =new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('ok')
},2000)
})
testPromise.then((res)=>{
console.log(res);
})
可以觀測到,在2s後,將會在控制檯打印出來ok。 首先,我們要確定的最關鍵的一點是,Promise沒有解決回撥函式的問題,可以看到我們的執行方案依舊是根據回撥函式來解決的,但是我們的方案更加的優雅一點、其次、如果我們註釋後面的程式碼執行以下程式碼
let testPromise =new Promise((resolve,reject)=>{
console.log('promise');
setTimeout(()=>{
console.log('ok');
resolve('ok')
},2000)
})
// testPromise.then((res)=>{
// console.log(res);
// })
會很快的打印出來 promise 在過了2s後打印出來OK。可見。Promise是在new的時候就執行了,而不是在then的時候。 同時我們觀測我們的程式碼可以有以下的結論
- Promise是一個類,需要使用new建立
- Promise在new的時候接收一個引數,該引數是一個函式executor
- executor接收兩個引數,這兩個引數也是一個函式,分別為resolve跟reject
- Promise生成的物件裡面有一個屬性是then。它接收兩個回撥函式onFulfilled跟onRejected
- 如果存在非同步的情況,那麼then中回撥函式不會立即執行、可見必有快取機制
- 在resolve結束之後執行onFulfilled,在reject函式之後執行onRejected,並且只會執行其中一個函式
那麼我們按照這個思路來寫一個Promise吧 1.
class MyPromise{
}
MyPromise應該是一個類 2.
class MyPromise{
constructor(exector){
exector(resolve,reject)
}
}
可是我們的resolve跟reject在哪裡呢? 很明顯無法接受外部的引數,那麼就只能是我們MyPromise內部的函數了 3.
class MyPromise{
constructor(exector){
exector(resolve,reject)
}
resolve(){
}
reject(){
}
}
class MyPromise{
constructor(exector){
exector(resolve,reject)
}
resolve(){
}
reject(){
}
then(onFulfilled,onRejected){
}
}
class MyPromise{
constructor(exector){
this.onFulfilledCallbacks=[]
this.onRejectedCallbacks=[]
exector(resolve,reject)
}
resolve(){
}
reject(){
}
then(onFulfilled,onRejected){
this.onFulfilledCallbacks.push(onFulfilled)
this.onRejectedCallbacks.push(onRejected)
}
}
在resolve之後執行onFulfilled 在這裡我們就需要引入狀態機制了。我們知道Promise是有狀態的,而resolve其實就是把Promise的狀態從pedding變成fulfilled而reject則是變成rejected
class MyPromise{
constructor(exector){
this.onFulfilledCallbacks=[]
this.onRejectedCallbacks=[]
this.state='pedding'
this.value=null //成功的回撥值
this.reason=null //成功的回撥值
exector(resolve,reject)
}
resolve(value){
if(this.state==='pedding'){
this.state='fulfilled'
this.value=value
this.onFulfilledCallbacks.forEach(fn=>fn(value))
}
}
reject(reason){
if(this.value==='pedding'){
this.state='rejected'
this.reason=reason
this.onRejectedCallbacks.forEach(fn=>fn(reason))
}
}
then(onFulfilled,onRejected){
this.onFulfilledCallbacks.push(onFulfilled)
this.onRejectedCallbacks.push(onRejected)
}
}
我們可以測試一下
let testPromise =new MyPromise((resolve,reject)=>{
setTimeout(()=>{
resolve('ok')
},2000)
})
testPromise.then((res)=>{
console.log('then');
console.log(res);
})
可以發現一些問題因為執行環境的問題,所以this的指向是有問題的,我們可以手動繫結一下
class MyPromise{
constructor(exector){
this.onFulfilledCallbacks=[]
this.onRejectedCallbacks=[]
this.state='pedding'
this.value=null //成功的回撥值
this.reason=null //成功的回撥值
exector(this.resolve.bind(this),this.reject.bind(this))
}
resolve(value){
if(this.state==='pedding'){
this.state='fulfilled'
this.value=value
this.onFulfilledCallbacks.forEach(fn=>fn(value))
}
}
reject(reason){
if(this.state==='pedding'){
this.state='rejected'
this.reason=reason
this.onRejectedCallbacks.forEach(fn=>fn(reason))
}
}
then(onFulfilled,onRejected){
this.onFulfilledCallbacks.push(onFulfilled)
this.onRejectedCallbacks.push(onRejected)
}
}
這樣實際上執行結果就符合我們的預期了 但是其實還是有些問題的
- 不是很符合PromiseA+的規範
- reject跟resolve函式其實不應該被返回
- 是否在一開始的時候就存在fulfilled狀態呢?
下面給出一版本更規範一點的實現
class MyPromise{
constructor(exector){
this.onFulfilledCallbacks=[]
this.onRejectedCallbacks=[]
this.state='pedding'
this.value=null //成功的回撥值
this.reason=null //成功的回撥值
let resolve=(value)=>{
if(this.state==='pedding'){
this.state='fulfilled'
this.value=value
this.onFulfilledCallbacks.forEach(fn=>fn(value))
}
}
let reject=(reason)=>{
if(this.state==='pedding'){
this.state='rejected'
this.reason=reason
this.onRejectedCallbacks.forEach(fn=>fn(reason))
}
}
exector(resolve,reject)
}
then(onFulfilled,onRejected){
if(this.state==='fulfilled'){
onFulfilled(this.value)
}
if(this.state==='rejected'){
onRejected(this.reason)
}
if(this.state==='pedding'){
this.onFulfilledCallbacks.push(onFulfilled)
this.onRejectedCallbacks.push(onRejected)
}
}
}
let testPromise =new MyPromise((resolve,reject)=>{
setTimeout(()=>{
resolve('ok')
},2000)
})
testPromise.then((res)=>{
console.log('then');
console.log(res);
})
關於PromiseA+規範我們可以參考
這樣我們就實現了一個基本具有Promise樣子的MyPromise。但是。回到開頭我們提到了Promise依舊是依靠回撥函式來執行的,這個樣子跟我們使用回撥函式的區別不是很大,為什麼還要寫Promise呢?原因在於Promise的精髓。鏈式呼叫 也就是
let testPromise =new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('ok')
},2000)
})
testPromise.then((res)=>{
console.log(res);
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('ok2')
},2000)
})
}).then((res)=>{
console.log(res);
})
下一篇文章我們將會研究一下Promise是怎麼實現鏈式呼叫的