中断promise
中斷或取消 Promise 鏈
Promise 已經成為了 JavaScript 管理異步操作的重要工具之一. 然而, 有的時候還是會很頭痛:
Promise// 等價于 `Promise.resolve(undefined).then`..then(() => {// 開始.}).then(() => {if (wantToBreakHere) {// 怎樣在這里終止這個 Promise 鏈?}}).then(() => {// 一定條件下不想被執行的代碼.});當然我們可以嵌套后面的 then, 但如果整條鏈很長很厚, 也必然很痛苦.
不過很多 Promise 實現都有一個 catch 方法, 我們可以做一點點小動作:
/** 用于中斷的信號 */ class BreakSignal { }Promise.then(() => {// 開始.}).then(() => {if (wantToBreakHere) {// 拋出中斷信號.throw new BreakSignal();}}).then(() => {// 需要跳過的部分.})// 接住中斷信號..catch(BreakSignal, () => { });其實個人覺得這么干還是比較優雅的, 但如果中間還有其他的 onrejected handler, 則需要手動傳遞 BreakSignal (再次拋出).
現在考慮另一種不同于 break 的情形:
page.on('load', () => {Promise.then(() => asyncMethodA()).then(result => asyncMethodB(result)).then(result => {// 更新一些東西...}); });如果 load 事件在短時間內觸發了兩次, 我們則需要取消前一個 Promise 鏈, 或者至少要防止它執行完成后做一些不該做的事情 (比如更新數據或者 UI).
那我們可以考慮這么處理:
class BreakSignal { }let context;/** 創建包含上下文信息的 wrapper */ function createWrapper() {let currentContext = context;return function (handler) {return function () {if (context !== currentContext) {throw new BreakSignal();}return handler.apply(undefined, arguments);};}; }page.on('unload', () => {context = undefined; });page.on('load', () => {context = {};let wrap = createWrapper();Promise// 包起來~.then(wrap(asyncMethodA)).then(wrap(asyncMethodB)).then(result => {// 更新一些東西...}).catch(BreakSignal, () => { }); });ThenFail
因為平時用的是自己的 Promise 實現 - ThenFail - 所以自己有需求一定要滿足自己. 我愉快地添加了偽 break 語句.
import { Promise } from 'thenfail';Promise.then(() => {// 開始.}).then(() => {if (wantToBreakHere) {// 就是這么任性.Promise.break;}return Promise.then(() => {Promise.break;}).then(() => {// 這里永遠也不會執行.});// 嵌套的情況不需要最下面的 `enclose()`.}).then(() => {// 需要跳過的部分.})// 結束當前的 Promise 鏈的上下文 (context), 避免 `break` 掉太多.// 如果這個 Promise 鏈會被返回, 交給不在你控制范圍內的代碼, 那么 enclose 操作非常重要~.enclose();其實還能 break ThenFail 的 each helper:
Promise.each([1, 2, 3], value => {if (value > 1) {Promise.break;// 或者異步地 `break`:return Promise.then(() => {// 做一些事情.}).break;}}).then(completed => {if (completed) {// 一些代碼...}});當然有的同學可能看出來了, Promise.break 這個偽語句實際上是一個會拋出異常的 getter, 拋出的異常則是類似于 BreakSignal 的這么個東西 (雖然實現上一個是 object1 === object2, 一個是 object instanceof Class).
前面有提到 ThenFail 中上下文的概念, 如果需要取消相同上下文的整個 Promise 鏈, 只需要釋放對應的 context 即可.
let context;page.on('unload', () => {context.dispose(); });page.on('load', () => {let promise = Promise.then(() => asyncMethodA()).then(result => asyncMethodB(result)).then(result => {// 更新一些東西...});context = promise.context; });另外, 釋放上下文也會釋放嵌套的上下文.
總結
- 上一篇: 在 Ubuntu 上安装 Protobu
- 下一篇: C# List<T>用法详解