《JavaScript高级程序设计》(第4版)第11章 期约与异步函数中期约扩展一节期约取消一节作者介绍“取消令牌”方法:
es6被认为是激进的,只要期约的逻辑开始执行,就没有办法阻止它执行到完成。作者介绍了实现取消期约的方法是在现有实现基础上提供一种临时性的封装。Kevin Smith提到的“取消令牌”(cancel token)。生成的令牌实例提供了一个接口,利用这个接口可以取消期约;同时也提供了一个期约实例,可以用来触发取消后的操作并求值取消状态。
下面是CancelToken类的一个基本实例:
class CancelToken {
constructor(cancelFn) {
this.promise = new Promise((resolve, reject) => {
cancelFn(resolve);
});
}
}
这个类包装了一个期约,把解决方法暴露给了cancelFn参数。这样,外部代码就可以向构造函数中传入一个函数,从而控制什么情况下可以取消期约。这里期约是令牌类的公共成员,因此可以给它添加处理程序以取消期约。
这个类大概可以这样使用:
<button id = "start">Start</button>
<button id = "cancel">Cancel</button>
<script>
class CancelToken {
constructor(cancelFn) {
this.promise = new Promise((resolve, reject) => {
cancelFn(() => {
setTimeout(console.log, 0, "delay cancelled");
resolve();
});
});
}
}
const startButton = document.querySelector('#start');
const cancelButton = document.querySelector('#cancel');
function cancellableDelayedResolve(delay) {
setTimeout(console.log, 0, "set delay");
return new Promise((resolve, reject) => {
const id = setTimeout(() => {
setTimeout(console.log, 0, "delayed resolve");
resolve();
}, delay);
const cancelToken = new CancelToken((cancelCallback) => cancelButton.addEventListener("click", cancelCallback));
cancelToken.promise.then(() => clearTimeout(id));
});
}
startButton.addEventListener("click", () => cancellableDelayedResolve(3000));
</script>
问题:
不用取消令牌,将代码改为:
<button id = "start">Start</button>
<button id = "cancel">Cancel</button>
<script>
const startButton = document.querySelector('#start');
const cancelButton = document.querySelector('#cancel');
function cancellableDelayedResolve(delay) {
setTimeout(console.log, 0, "set delay");
return new Promise((resolve, reject) => {
const id = setTimeout(() => {
setTimeout(console.log, 0, "delayed resolve");
resolve();
}, delay);
cancelButton.addEventListener("click", () => {
clearTimeout(id);
console.log("delay cancelled");
});
});
}
startButton.addEventListener("click", () => cancellableDelayedResolve(3000));
</script>
这样也是可以实现取消期约的,那为什么要用令牌呢?