但有一个微妙的挑战:从设计上讲,Promises 拥抱了其异步性质,一旦启动就无法直接停止。
在我们需要更精细控制的情况下,这可能会成为障碍,这时,我们就需要设计可取消的 Promises!
原始 Promise 的问题
想象一下获取大文件或发出网络请求。如果用户决定在中途取消,该怎么办?使用标准 Promise,会陷入困境。它会在后台继续运行,可能会浪费资源。
我们的目标:优雅取消
我们希望为我们的 Promise 提供以下功能:
- 取消:发起取消请求。
- 指示状态:了解 Promise 是否已成功取消或已解决。
TaskCancelable 解决方案
以下是我们可以实现 TaskCancelable 函数来增强我们的 Promise 的方法:
export const TaskCancelable = (task) => {
let _reject;
let isCancel = false;
const _status = Symbol("cancel");
const cancelP = new Promise((resolve, reject) => {
_reject = reject;
});
const p = Promise.race([task, cancelP]);
p.catch((reason) => {
if (reason === _status) {
isCancel = true;
}
});
p.cancel = () => {
_reject(_status);
return p;
};
p.isCancel = () => {
return isCancel;
};
return p;
};
解释
Symbol("cancel"):我们使用 Symbol 来创建取消的唯一标识符。这可以防止与其他潜在错误发生冲突。
Promise.race([task, cancelP]):奇迹就在这里发生!我们将原始任务 Promise 与 cancelP Promise 进行比赛。先解决或先拒绝的一方获胜。
p.cancel():此自定义函数在调用时会拒绝 cancelP Promise,从而有效触发取消。
p.isCancel():这个方便的方法让我们可以检查 Promise 是否确实被取消。
实际示例
让我们看看如何使用 TaskCancelable:
// Simulate a long-running task
const longTask = new Promise((resolve) => {
setTimeout(() => {
resolve("Task complete!");
}, 3000);
});
const cancellableTask = TaskCancelable(longTask);
// Cancel after 1 second
setTimeout(() => {
cancellableTask.cancel();
}, 1000);
cancellableTask
.then((result) => console.log("Success:", result)) // Won't run
.catch(() => {
if (cancellableTask.isCancel()) {
console.log("Task was cancelled!");
} else {
console.log("Task failed for another reason");
}
});
结论
通过创建可取消的 Promises,我们在管理异步操作方面获得了更大的灵活性。这种模式对于响应式用户界面和资源密集型任务至关重要,因为取消是这些任务的关键。