如何在Javascript承诺中取消超时
How to cancel timeout inside of Javascript Promise?
我在JavaScript中玩承诺,并试图承诺setTimeout函数:
function timeout(ms) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('timeout done');
}, ms);
});
}
var myPromise=timeout(3000);
myPromise.then(function(result) {
console.log(result); // timeout done
})
相当简单,但我想知道如何在承诺解决之前取消超时。timeout
返回Promise
对象,因此我可以访问setTimeout
返回的值,并且不能通过clearTimeout
取消超时。最好的方法是什么?
顺便说一句,没有真正的目的,我只是想知道这将如何处理。我把它放在这里http://plnkr.co/edit/NXFjs1dXWVFNEOeCV1BA?p=preview
Edit 2021所有平台都将AbortController作为取消原语,并且有一些内置支持。
在node . js// import { setTimeout } from 'timers/promises' // in ESM
const { setTimeout } = require('timers/promises');
const ac = new AbortController();
// cancellable timeout
(async () => {
await setTimeout(1000, null, { signal: ac.signal });
})();
// abort the timeout, rejects with an ERR_ABORT
ac.abort();
浏览器中你可以填充这个API,并使用与上面的例子相同的方法:
function delay(ms, value, { signal } = {}) {
return new Promise((resolve, reject) => {
const listener = () => {
clearTimeout(timer);
reject(signal.reason);
};
signal?.throwIfAborted();
const timer = setTimeout(() => {
signal?.removeEventListener('abort', listener);
resolve(value);
}, ms);
signal?.addEventListener('abort', listener);
});
}
你可以做的是,你可以从timeout
函数返回一个取消器,并在需要时调用它。这样,您就不需要全局(或外部作用域)存储timeoutid
,而且还可以管理对该函数的多次调用。函数timeout
返回的每个对象实例都有自己的取消器来执行取消。
function timeout(ms) {
var timeout, promise;
promise = new Promise(function(resolve, reject) {
timeout = setTimeout(function() {
resolve('timeout done');
}, ms);
});
return {
promise:promise,
cancel:function(){clearTimeout(timeout );} //return a canceller as well
};
}
var timeOutObj =timeout(3000);
timeOutObj.promise.then(function(result) {
console.log(result); // timeout done
});
//Cancel it.
timeOutObj.cancel();
Plnkr
PSL的答案是正确的,然而,有一些注意事项,我想做的有点不同。
- 超时被清除意味着代码不会运行-所以我们应该拒绝承诺。
- 在我们的例子中不需要返回两个东西,我们可以在JavaScript中进行monkey patch。
:
function timeout(ms, value) {
var p = new Promise(function(resolve, reject) {
p._timeout = setTimeout(function() {
resolve(value);
}, ms);
p.cancel = function(err) {
reject(err || new Error("Timeout"));
clearTimeout(p._timeout); // We actually don't need to do this since we
// rejected - but it's well mannered to do so
};
});
return p;
}
我们可以这样做:
var p = timeout(1500)
p.then(function(){
console.log("This will never log");
})
p.catch(function(){
console.log("This will get logged so we can now handle timeouts!")
})
p.cancel(Error("Timed out"));
可能会对完全取消感兴趣,实际上一些库直接将其作为库的一个特性来支持。事实上,我敢说大多数人都是这样。然而,这会引起干扰问题。引用KrisKowal的话:
我对取消的立场已经改变了。我现在确信取消(bg:传播)在承诺抽象中本质上是不可能的,因为承诺可以多重依赖,并且依赖可以在任何时候引入。如果任何依赖项取消了一个承诺,它将能够干扰未来的依赖项。有两种方法可以解决这个问题。一种是引入一个单独的取消"能力",或许可以作为一个论据。另一种方法是引入一个新的抽象,一个可能的"任务",作为交换,它要求每个任务只有一个观察者(一个然后调用,永远),可以取消而不必担心干扰。任务将支持fork()方法来创建一个新任务,允许另一个依赖者保留该任务或推迟取消。
以上对@Benjamin和@PSL的回答是有效的,但是如果您需要在内部取消可取消的超时时由外部源使用该怎么办?
例如,交互可能看起来像这样:
// externally usage of timeout
async function() {
await timeout() // timeout promise
}
// internal handling of timeout
timeout.cancel()
我自己也需要这样的实现,所以我想出了这样的方法:
/**
* Cancelable Timer hack.
*
* @notes
* - Super() does not have `this` context so we have to create the timer
* via a factory function and use closures for the cancelation data.
* - Methods outside the consctutor do not persist with the extended
* promise object so we have to declare them via `this`.
* @constructor Timer
*/
function createTimer(duration) {
let timerId, endTimer
class Timer extends Promise {
constructor(duration) {
// Promise Construction
super(resolve => {
endTimer = resolve
timerId = setTimeout(endTimer, duration)
})
// Timer Cancelation
this.isCanceled = false
this.cancel = function() {
endTimer()
clearTimeout(timerId)
this.isCanceled = true
}
}
}
return new Timer(duration)
}
现在你可以这样使用计时器:
let timeout = createTimer(100)
并在其他地方取消承诺:
if (typeof promise !== 'undefined' && typeof promise.cancel === 'function') {
timeout.cancel()
}
这是我在TypeScript中的回答:
private sleep(ms) {
let timerId, endTimer;
class TimedPromise extends Promise<any> {
isCanceled: boolean = false;
cancel = () => {
endTimer();
clearTimeout(timerId);
this.isCanceled = true;
};
constructor(fn) {
super(fn);
}
}
return new TimedPromise(resolve => {
endTimer = resolve;
timerId = setTimeout(endTimer, ms);
});
}
用法:
const wait = sleep(10*1000);
setTimeout(() => { wait.cancel() },5 * 1000);
await wait;
- 如何在angularJS中编辑时,如果DB中的值为true,则设置复选框,如果值为false,则取消选中复选框
- jquery点击函数select&取消选择
- 更改路线后取消angularjs超时不起作用
- 取消/停止超时功能
- React Native timemixin 取消计时器超时
- 由于超时,预渲染请求已取消
- 正在取消本机android会话超时警告
- 如果为true,则设置超时,否则取消超时
- 在揭示模块模式中取消失控的JavaScript超时/间隔
- 取消设置为ID的多个超时
- 如果按下按钮,我如何取消超时?
- 如何在Javascript承诺中取消超时
- 取消Javascript超时
- . js承诺,取消超时
- Karma AngularJs取消/模拟超时E2E
- 延迟、取消或超时一个事件
- 取消路由更改上的AngularJS$超时
- 在编辑过程中取消超时
- 在给定的超时后,取消工具提示
- Javascript导航,悬停显示,取消悬停隐藏与超时,如何