Promise:為什么沒有取消?
共 6477字,需瀏覽 13分鐘
·
2024-07-16 09:15
點擊上方 前端Q,關(guān)注公眾號
回復(fù)加群,加入前端Q技術(shù)交流群
在JavaScript中,Promise是用于處理異步操作的對象,它代表一個異步操作的最終完成(或失敗)及其結(jié)果值。然而,JavaScript的Promise并不提供內(nèi)置的取消(cancel)機制。
Promise是經(jīng)過了深思熟慮,才不自帶取消功能的!!!
這篇文章,將圍繞著設(shè)計的哲學(xué),以及從狀態(tài)機的角度,解釋為什么不需要cancel。
即使如此,文章最后部分,還是會提供一些方法,來實現(xiàn)一下cancle。
設(shè)計的哲學(xué)
設(shè)計理念
-
Promise的設(shè)計初衷是為了簡化回調(diào)函數(shù)的使用,使得處理異步操作的代碼更加簡潔和可讀。其設(shè)計重點在于處理異步操作的成功和失敗,而不是控制操作的生命周期。 -
取消機制會引入復(fù)雜性,尤其是對于依賴于多個Promise的情況,例如Promise.all或Promise.race。如果某個Promise被取消,其影響可能會傳遞給其他依賴于它的Promise,導(dǎo)致意外的行為和難以調(diào)試的問題。
資源管理
-
異步操作通常涉及到外部資源,如網(wǎng)絡(luò)請求、定時器等。Promise取消機制需要能夠正確管理和釋放這些資源。實現(xiàn)一個通用且可靠的資源管理機制非常復(fù)雜,并且可能因不同的資源類型而異。
取消語義不明確
-
如果一個Promise可以被取消,那么需要明確如何處理其已完成的狀態(tài)。特別是,處理已經(jīng)部分完成或即將完成的操作,可能會導(dǎo)致不一致的狀態(tài)。
狀態(tài)機:簡單就是美
Promise的狀態(tài)機
在輸入一個狀態(tài)時,只得到一個固定的狀態(tài)。
一個Promise可以被看作是一個簡單的狀態(tài)機,它有以下幾種狀態(tài):
-
Pending(進行中) :初始狀態(tài),表示異步操作尚未完成。 -
Fulfilled(已完成) :表示異步操作成功完成,并返回了一個值。 -
Rejected(已拒絕) :表示異步操作失敗,并返回了一個原因(錯誤)。
狀態(tài)轉(zhuǎn)換規(guī)則如下:
-
從Pending狀態(tài)可以轉(zhuǎn)換到Fulfilled狀態(tài)。 -
從Pending狀態(tài)可以轉(zhuǎn)換到Rejected狀態(tài)。 -
一旦轉(zhuǎn)換到Fulfilled或Rejected狀態(tài),Promise的狀態(tài)就不可再改變。
取消功能的復(fù)雜性
引入取消功能意味著需要增加一個新的狀態(tài)——“Cancelled(已取消)”。這會使?fàn)顟B(tài)機的設(shè)計變得更加復(fù)雜,因為需要考慮更多的狀態(tài)轉(zhuǎn)換和邊界情況。
如果我們引入“Cancelled”狀態(tài),狀態(tài)機的狀態(tài)和轉(zhuǎn)換規(guī)則將變成:
-
Pending(進行中) :
-
-
可以轉(zhuǎn)換到Fulfilled。 -
可以轉(zhuǎn)換到Rejected。 -
可以轉(zhuǎn)換到Cancelled。
-
Fulfilled(已完成) :狀態(tài)不可變。 -
Rejected(已拒絕) :狀態(tài)不可變。 -
Cancelled(已取消) :狀態(tài)不可變。
這種增加的復(fù)雜性會導(dǎo)致以下問題:
-
狀態(tài)轉(zhuǎn)換沖突:需要明確地處理在Pending狀態(tài)下多次轉(zhuǎn)換的情況。例如,如果一個Promise在Pending狀態(tài)下同時嘗試轉(zhuǎn)換到Fulfilled和Cancelled,應(yīng)該優(yōu)先處理哪一個? -
副作用處理:許多異步操作(如網(wǎng)絡(luò)請求、文件讀寫等)具有副作用。取消這些操作需要確保所有相關(guān)的資源都被正確地清理,這不僅增加了實現(xiàn)的復(fù)雜性,還可能導(dǎo)致不一致的狀態(tài)。 -
鏈式操作:Promise通常被鏈式調(diào)用( .then().catch() )。如果一個中間的Promise被取消,如何處理后續(xù)鏈式操作也是一個難題。例如,Promise.all或Promise.race的行為如何改變?
如何實現(xiàn)取消功能
盡管標(biāo)準的Promise沒有內(nèi)置的取消功能,可以通過一些方法來實現(xiàn)類似的功能。例如,使用AbortController來取消網(wǎng)絡(luò)請求,或者使用自定義的Promise包裝器來支持取消。
使用AbortController
對于Fetch API,可以使用AbortController來取消請求:
const controller = new AbortController();
const signal = controller.signal;
fetch('https://www.baidu.com', { signal })
.then(response => response)
.then(data => console.log(data))
.catch(err => {
if (err.name === 'AbortError') {
console.log('Fetch aborted');
} else {
console.error('Fetch error:', err);
}
});
// 取消請求
controller.abort();
自定義Promise包裝器
也可以創(chuàng)建一個支持取消的自定義Promise包裝器:
class CancellablePromise {
constructor(executor) {
this._hasCanceled = false;
this._promise = new Promise((resolve, reject) => {
executor(
value => this._hasCanceled ? reject({ canceled: true }) : resolve(value),
reason => this._hasCanceled ? reject({ canceled: true }) : reject(reason)
);
});
}
cancel() {
this._hasCanceled = true;
}
then(onFulfilled, onRejected) {
return this._promise.then(onFulfilled, onRejected);
}
catch(onRejected) {
return this._promise.catch(onRejected);
}
}
// 使用自定義的CancellablePromise
const cancellablePromise = new CancellablePromise((resolve, reject) => {
setTimeout(() => resolve('Completed!'), 1000);
});
cancellablePromise.then(
result => console.log(result),
err => {
if (err.canceled) {
console.log('Promise was canceled');
} else {
console.error('Promise error:', err);
}
}
);
// 取消Promise
cancellablePromise.cancel();
雖然標(biāo)準的Promise沒有內(nèi)置取消功能,但可以通過這些方法來實現(xiàn)取消邏輯,根據(jù)實際需求選擇合適的方案。
結(jié)語
雖然JavaScript的Promise沒有內(nèi)置取消功能,但這并不意味著我們無法實現(xiàn)取消功能。通過理解Promise的設(shè)計哲學(xué)和狀態(tài)機模型,我們可以更好地掌握其使用方法,并通過巧妙的編程技巧實現(xiàn)我們需要的功能
本文轉(zhuǎn)載于掘金-德瑪西亞大寶劍之力
原文:https://juejin.cn/post/7373986431850872869
往期推薦
最后
歡迎加我微信,拉你進技術(shù)群,長期交流學(xué)習(xí)...
歡迎關(guān)注「前端Q」,認真學(xué)前端,做個專業(yè)的技術(shù)人...
