在現代前端開發中,Promise 和 async/await
已經成為處理異步操作的基石。然而,一個常見的棘手問題是:如何取消一個已經開始的 Promise?
比如,用戶發起一個數據請求,但在請求完成前又導航到了其他頁面;或者用戶在一個搜索框中快速輸入,我們需要取消前一次的搜索請求,只保留最后一次。在這些場景下,取消一個進行中的 Promise 就顯得至關重要。
核心問題:為什么 Promise 本身不可取消?
首先,我們需要理解 Promise 的核心設計理念。一個 Promise 代表一個異步操作的最終結果。它的狀態一旦從 pending
(進行中)變為 fulfilled
(已成功)或 rejected
(已失敗),就永遠不會再改變。
Promise 本身不提供取消機制,原因如下:
- 狀態不可逆:這是 Promise 的核心規范。一旦狀態改變,就形成了一個確定的、不可變的結果。
- 單一責任:Promise 的職責是傳遞價值和狀態,而不是控制異步操作本身的執行流程。發起異步操作的函數(如
fetch
)才是執行者。
打個比方:你寄出了一封信(發起了一個 Promise),你不能在信件投遞過程中把它神奇地從郵政系統里撤回來。你能做的,是在信件送達時(Promise 完成時),選擇忽略它。
我們的目標,就是實現這種“忽略”機制,并盡可能地通知底層的異步操作停止工作,以節省資源。
AbortController
AbortController
是目前實現 Promise 取消的最佳實踐和標準方案。它最初是為取消 fetch
請求而設計的,但其通用性使其可以與任何異步操作集成。
AbortController
的工作方式:
- 創建一個
AbortController
實例。 controller.signal
:這是一個 AbortSignal
對象,可以傳遞給需要支持取消的異步函數(如 fetch
)。controller.abort()
:調用此方法來發出“中止”信號。- 當
abort()
被調用時,signal
會通知所有監聽它的異步操作。對于 fetch
來說,它會自動中止網絡請求并讓 Promise reject 一個名為 AbortError
的錯誤。
1. 與 fetch
配合使用
這是 AbortController
最常見的用法。
在 async/await
語法中同樣清晰:
2. 在自定義 Promise 中使用 AbortController
你也可以讓你自己的異步函數支持 AbortSignal
。
原理:
- 你的函數需要接收
signal
作為參數。 - 在異步操作的關鍵節點,檢查
signal.aborted
屬性。如果為 true
,則提前退出。 - 使用
signal.addEventListener('abort', ...)
來注冊清理邏輯(如清除定時器)。
優點:
- 官方標準:是 W3C 和 WHATWG 定義的標準 API。
- 真正中止底層操作:
fetch
會中止網絡連接,自定義函數也可以通過它來清理資源(如清除定時器),避免了不必要的浪費。 - 語義清晰:通過專門的
AbortError
來區分“取消”和“其他錯誤”,代碼更健壯。 - 組合性強:一個
AbortSignal
可以傳遞給多個 Promise,實現批量取消。
雖然 Promise 本身的核心設計使其不可變,但通過 AbortController
這一強大的模式,我們已經可以非常有效地控制和終止異步流程,編寫出更健壯、更高效的應用程序。
閱讀原文:https://mp.weixin.qq.com/s/O4ej_zb_sKoJcCamCmmvAA
該文章在 2025/6/13 9:29:09 編輯過