一步一步實現手寫Promise
一步一步手寫實現Promise
Promise雛形
首先我們根據原生Promise的使用方法來分析
new Promise ((reslove, reject) => {
resolve('resolve')
reject('reject')
})
Promise 建構函式只有一個引數,是一個函式,我們將它命名為executor( )。這個函式在構造之後會直接被執行,所以我們稱之為起始函式。起始函式包含兩個引數 resolve 和 reject。Promise 物件有以下兩個特點:
1、物件的狀態不受外界影響。Promise 物件代表一個非同步操作,有三種狀態:
- pending: 初始狀態,不是成功或失敗狀態。
- fulfilled: 意味著操作成功完成。
- rejected: 意味著操作失敗。
只有非同步操作的結果,可以決定當前是哪一種狀態,任何其他操作都無法改變這個狀態。這也是 Promise 這個名字的由來,它的英語意思就是「承諾」,表示其他手段無法改變。
2、一旦狀態改變,就不會再變,任何時候都可以得到這個結果。Promise 物件的狀態改變,只有兩種可能:從 Pending 變為 Resolved 和從 Pending 變為 Rejected。只要這兩種情況發生,狀態就凝固了,不會再變了,會一直保持這個結果(狀態保護)。
那麼我們可以可以根據這三種不同狀態去實現resolve、reject,以及實現then方法,那麼一個簡單的promise雛形就出來了。下面來實現它:
class Promi{
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(executor) {
this.status = Promi.PENDING
this.value = null
//對executor()進行異常捕獲,交給reject()處理
try {
executor(this.resolve.bind(this), this.reject.bind(this))
} catch (error) {
this.reject(error)
}
}
resolve(value) {
//if是為了進行狀態保護
if(this.status == Promi.PENDING){
this.status = Promi.FULFILLED
this.value = value
}
}
reject(reason) {
if(this.status == Promi.PENDING){
this.status = Promi.REJECTED
this.value = reason
}
}
}
then( )的基礎構建
對於已經例項化過的 promise 物件可以呼叫 promise.then() 方法,還是先觀察原生Promise裡的用法
promise.then(onFulfilled, onRejected)
於是我們對then( )進行基礎構建,
then(onFulfilled, onRejected) {
if(this.status == Promi.FULFILLED) {
onFulfilled(this.value)
}
if(this.status == Promi.REJECTED) {
onRejected(this.value)
}
}
可是我們在使用promise物件時常常碰到這種情況
let p = new Promi((resolve, reject) => {
reject("reject")
})
p.then(value => {
console.log('成功', value)
})
即onFulfilled和onRejected兩個引數是可以不傳的,如果我們直接用上面的寫法,會導致報錯,因為status變為了REJECTED卻不能呼叫對應的onRejected()函式。於是我們對then( )方法進行以下改寫。
then(onFulfilled, onRejected) {
//如果傳遞的引數為null,將其封裝成函式
if (typeof onFulfilled != 'function') {
onFulfilled = () => {}
}
if (typeof onRejected != 'function') {
onRejected = () => {}
}
if(this.status == Promi.FULFILLED) {
onFulfilled(this.value)
}
if(this.status == Promi.REJECTED) {
onRejected(this.value)
}
}
then( )的非同步執行
進行到這裡,我們不能忘記Promise的初衷是為了更加優雅地書寫複雜的非同步任務。現在這樣的寫法會使then方法阻塞我們的同步任務。於是我們在then中onFulfilled和onRejected方法的執行外面包上一個計時器,這樣便不會立即執行方法,而是將其放入任務佇列中,等待同步任務執行完成後對其進行輪詢。
then(onFulfilled, onRejected) {
//如果傳遞的引數為null,將其封裝成函式
if (typeof onFulfilled != 'function') {
onFulfilled = () => {}
}
if (typeof onRejected != 'function') {
onRejected = () => {}
}
if(this.status == Promi.FULFILLED) {
setTimeout(() => {
try {
onFulfilled(this.value)
} catch (error) {
onRejected(error)
}
})
}
if(this.status == Promi.REJECTED) {
setTimeout(() => {
try {
onRejected(this.value)
} catch (error) {
onRejected(error)
}
})
}
}
}
promise的pending狀態處理
下面我們看看這種情況
let p = new Promi((resolve, reject) => {
setTimeout(() => {
reject("拒絕")
}, 1000)
})
p.then(value => {
console.log(value)
},
reason => {
console.log(reason)
})
console.log("同步任務")
如果executor( )中包含一個計時器,即this.status的改變有延時,那按照之前的寫法,在then方法中不會執行任何一個回撥函式。我們在這裡需要在Promi類中增加一個屬性:callbacks陣列,來儲存我們在then中的回撥。當promise在pending狀態時將回撥函式壓入棧中,當執行reslove()或reject()時再進行呼叫。
then()中新增
if(this.status == Promi.PENDING) {
this.callbacks.push({
onFulfilled,
onRejected
})
}
reslove()和reject()改寫為
resolve(value) {
//if是為了進行狀態保護
if(this.status == Promi.PENDING){
this.status = Promi.FULFILLED
this.value = value
setTimeout(() => {
this.callbacks.map(callback => {
callback.onFulfilled(this.value)
})
})
}
}
reject(reason) {
if(this.status == Promi.PENDING){
this.status = Promi.REJECTED
this.value = reason
setTimeout(() => {
this.callbacks.map(callback => {
callback.onRejected(this.value)
})
})
}
}
注意還是要在呼叫onFulfilled和onRejected外面包裹一層定時器以保證then的非同步執行。
promise的鏈式呼叫
promise的鏈式呼叫是其非常重要的特性。其原理是then()方法會返回一個新的Promise例項,所以then()方法後面可以繼續跟另一個then()方法進行鏈式呼叫。
then(onFulfilled, onRejected) {
//如果傳遞的引數為null,將其封裝成函式
if (typeof onFulfilled != 'function') {
onFulfilled = () => {}
}
if (typeof onRejected != 'function') {
onRejected = () => {}
}
return new Promi((resolve,reject) => {
if(this.status == Promi.PENDING) {
this.callbacks.push({
onFulfilled: value => {
try {
let result = onFulfilled(value)
resolve(result)
} catch (error) {
reject(error)
}
},
onRejected: value => {
try {
let result = onRejected(value)
resolve(result)
} catch (error) {
reject(error)
}
}
})
}
if(this.status == Promi.FULFILLED) {
setTimeout(() => {
try {
let result = onFulfilled(this.value)
resolve(result)
} catch (error) {
reject(error)
}
})
}
if(this.status == Promi.REJECTED) {
setTimeout(() => {
try {
let result = onRejected(this.value)
resolve(result)
} catch (error) {
reject(error)
}
})
}
})
}
注意,狀態轉換時要呼叫resolve()和reject()才能將this.value傳遞給下一個then()。
相關文章
- js 真的是一步一步手寫promiseJSPromise
- promise原理—一步一步實現一個promisePromise
- 一步一步實現一個PromisePromise
- 一步一步實現一個Promise A+規範的 PromisePromise
- 一步一步手寫GPTGPT
- 一步一步實現一個Promise A+規範的 Promise之二:鏈式呼叫Promise
- 一步一步實現一個符合Promises/A+規範的PromisePromise
- 一步一步來:手寫Koa2
- 一步一步實現一個符合PromiseA+規範的Promise庫(2)Promise
- 一步一步實現一個符合PromiseA+規範的Promise庫(1)Promise
- 一步一步實現一個符合PromiseA+規範的Promise庫(3)Promise
- 手寫JQuery的框架的簡易實現(一步一步教你庫開發的架構設計)jQuery框架架構
- 用原生js手寫實現promiseJSPromise
- 一步一步,實現自己的ButterKnife(二)
- 一步一步實現單身狗雨
- 一步一步教你寫kubernetes sidecarIDE
- 帶你一步一步手寫一個簡單的Spring MVCSpringMVC
- 一步一步實現 .NET 8 部署到 DockerDocker
- 一步一步實現Vue資料繫結Vue
- 手寫實現滿足 Promise/A+ 規範的 PromisePromise
- 一步步教你實現Promise/A+ 規範 完整版Promise
- 一步一步搭建腳手架
- TensorFlow 一步一步實現卷積神經網路卷積神經網路
- 一步步寫一個符合Promise/A+規範的庫Promise
- 跟我一步一步實現 Flutter 視訊播放外掛 (一)Flutter
- 一步一步帶你實現一個canvas抽獎轉盤Canvas
- 一步一步教你實現iOS音訊頻譜動畫(二)iOS音訊動畫
- 一步一步教你實現iOS音訊頻譜動畫(一)iOS音訊動畫
- Android自定義View教你一步一步實現即刻點贊效果AndroidView
- 手寫PromisePromise
- 手寫 PromisePromise
- Vue雙向繫結原理,教你一步一步實現雙向繫結Vue
- Android自定義View教你一步一步實現薄荷健康滑動捲尺AndroidView
- 帶你一步一步手撕Spring MVC原始碼加手繪流程圖SpringMVC原始碼流程圖
- [手寫系列] 帶你實現一個簡單的PromisePromise
- 一步一步來
- Promise規範以及手寫PromisePromise
- (原創)【MAUI】一步一步實現“懸浮操作按鈕”(FAB,Floating Action Button)UI