Javascript 如果未能及时完成,NodeJS 超时承诺
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/32461271/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
NodeJS Timeout a Promise if failed to complete in time
提问by AlexD
How can I timeout a promise after certain amount of time? I know Q has a promise timeout, but I'm using native NodeJS promises and they don't have .timeout function.
如何在一定时间后超时承诺?我知道 Q 有一个承诺超时,但我使用的是原生 NodeJS 承诺,他们没有 .timeout 功能。
Am I missing one or its wrapped differently?
我错过了一个还是它的包装方式不同?
Alternatively, Is the below implementation good in means of not sucking up memory, actually working as expected?
或者,下面的实现在不占用内存方面是否很好,实际上按预期工作?
Also can I make it somehow wrapped globally so I can use it for every promise I create, without having to repeat the setTimeout and clearTimeout code?
我也可以让它以某种方式全局包装,以便我可以将它用于我创建的每个承诺,而不必重复 setTimeout 和 clearTimeout 代码?
function run() {
logger.info('DoNothingController working on process id {0}...'.format(process.pid));
myPromise(4000)
.then(function() {
logger.info('Successful!');
})
.catch(function(error) {
logger.error('Failed! ' + error);
});
}
function myPromise(ms) {
return new Promise(function(resolve, reject) {
var hasValueReturned;
var promiseTimeout = setTimeout(function() {
if (!hasValueReturned) {
reject('Promise timed out after ' + ms + ' ms');
}
}, ms);
// Do something, for example for testing purposes
setTimeout(function() {
resolve();
clearTimeout(promiseTimeout);
}, ms - 2000);
});
}
Thanks!
谢谢!
回答by T.J. Crowder
Native JavaScript promises don't have any timeout mechanism.
原生 JavaScript 承诺没有任何超时机制。
The question about your implementation would probably be a better fit for http://codereview.stackexchange.com, but a couple of notes:
关于您的实现的问题可能更适合http://codereview.stackexchange.com,但有几个注意事项:
You don't provide a means of actually doing anything in the promise, and
There's no need for
clearTimeout
within yoursetTimeout
callback, sincesetTimeout
schedules a one-off timer.Since a promise can't be resolved/rejected once it's been resolved/rejected, you don't need that check.
你没有提供在承诺中实际做任何事情的方法,并且
clearTimeout
在您的setTimeout
回调中不需要,因为setTimeout
安排了一次性计时器。由于承诺一旦被解决/拒绝就无法解决/拒绝,因此您不需要该检查。
So perhaps something along these lines:
所以也许是这样的:
function myPromise(ms, callback) {
return new Promise(function(resolve, reject) {
// Set up the real work
callback(resolve, reject);
// Set up the timeout
setTimeout(function() {
reject('Promise timed out after ' + ms + ' ms');
}, ms);
});
}
Used like this:
像这样使用:
myPromise(2000, function(resolve, reject) {
// Real work is here
});
(Or you maywant it to be a bit more complicated, see update under the line below.)
(或者您可能希望它更复杂一点,请参阅下一行的更新。)
I'd be slightly concerned about the fact that the semantics are slightly different (no new
, whereas you do use new
with the Promise
constructor), so you might adjust that.
我会稍微担心语义略有不同的事实(不new
,而您确实new
与Promise
构造函数一起使用),因此您可以对其进行调整。
The other problem, of course, is that most of the time, you don't want to construct new promises, and so couldn't use the above. Most of the time, you have a promise already (the result of a previous then
call, etc.). But for situations where you're really constructing a new promise, you could use something like the above.
当然,另一个问题是,大多数情况下,您不想构建新的 Promise,因此无法使用上述内容。大多数情况下,您已经有一个承诺(先前then
调用的结果等)。但是对于您真正构建新承诺的情况,您可以使用类似上面的内容。
You can deal with the new
thing by subclassing Promise
:
你可以new
通过子类化来处理这个事情Promise
:
class MyPromise extends Promise {
constructor(ms, callback) {
// We need to support being called with no milliseconds
// value, because the various Promise methods (`then` and
// such) correctly call the subclass constructor when
// building the new promises they return.
// This code to do it is ugly, could use some love, but it
// gives you the idea.
let haveTimeout = typeof ms === "number" && typeof callback === "function";
let init = haveTimeout ? callback : ms;
super((resolve, reject) => {
init(resolve, reject);
if (haveTimeout) {
setTimeout(() => {
reject("Timed out");
}, ms);
}
});
}
}
Usage:
用法:
let p = new MyPromise(300, function(resolve, reject) {
// ...
});
p.then(result => {
})
.catch(error => {
});
Live Example:
现场示例:
// Uses var instead of let and non-arrow functions to try to be
// compatible with browsers that aren't quite fully ES6 yet, but
// do have promises...
(function() {
"use strict";
class MyPromise extends Promise {
constructor(ms, callback) {
var haveTimeout = typeof ms === "number" && typeof callback === "function";
var init = haveTimeout ? callback : ms;
super(function(resolve, reject) {
init(resolve, reject);
if (haveTimeout) {
setTimeout(function() {
reject("Timed out");
}, ms);
}
});
}
}
var p = new MyPromise(100, function(resolve, reject) {
// We never resolve/reject, so we test the timeout
});
p.then(function(result) {
snippet.log("Resolved: " + result);
}).catch(function(reject) {
snippet.log("Rejected: " + reject);
});
})();
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
Both of those will call reject
when the timer expires even if the callback calls resolve
or reject
first. That's fine, a promise's settled state cannot be changed once it's set, and the spec defines calls to resolve
or reject
on a promise that's already settled as do-nothings that don't raise an error.
reject
即使回调调用resolve
或reject
首先调用,它们也会在计时器到期时调用。没关系,promise 的已解决状态一旦设置就无法更改,并且规范定义了对已解决的承诺的调用resolve
或reject
对已解决的承诺的调用,该承诺不会引发错误。
But if it bother you, you could wrap resolve
and reject
. Here's myPromise
done that way:
但如果它打扰你,你可以包装resolve
和reject
。这是myPromise
这样做的:
function myPromise(ms, callback) {
return new Promise(function(resolve, reject) {
// Set up the timeout
let timer = setTimeout(function() {
reject('Promise timed out after ' + ms + ' ms');
}, ms);
let cancelTimer = _ => {
if (timer) {
clearTimeout(timer);
timer = 0;
}
};
// Set up the real work
callback(
value => {
cancelTimer();
resolve(value);
},
error => {
cancelTimer();
reject(error);
}
);
});
}
You can spin that about 18 different ways, but the basic concept is that the resolve
and reject
we pass the promise executor we receive are wrappers that clear the timer.
你可以旋转,大约有18种,但基本概念是resolve
和reject
我们通过我们收到的承诺执行的包装是清零定时器。
But, that creates functions and extra function calls that you don't need. The spec is clearabout what the resolving functions do when the promise is already resolved; they quit quite early.
但是,这会创建您不需要的函数和额外的函数调用。规范明确了当承诺已经被解析时解析函数的作用;他们很早就辞职了。
回答by MinusFour
While maybe there's no support for a promise timeout, you could race promises:
虽然可能不支持承诺超时,但您可以竞争承诺:
var race = Promise.race([
new Promise(function(resolve){
setTimeout(function() { resolve('I did it'); }, 1000);
}),
new Promise(function(resolve, reject){
setTimeout(function() { reject('Timed out'); }, 800);
})
]);
race.then(function(data){
console.log(data);
}).catch(function(e){
console.log(e);
});
A generic Promise.timeout
:
一个通用的Promise.timeout
:
Promise.timeout = function(timeout, cb){
return Promise.race([
new Promise(cb),
new Promise(function(resolve, reject){
setTimeout(function() { reject('Timed out'); }, timeout);
})
]);
}
Example:
例子:
Promise.timeout = function(timeout, cb) {
return Promise.race([
new Promise(cb),
new Promise(function(resolve, reject) {
setTimeout(function() {
reject('Timed out');
}, timeout);
})
]);
}
function delayedHello(cb){
setTimeout(function(){
cb('Hello');
}, 1000);
}
Promise.timeout(800, delayedHello).then(function(data){
console.log(data);
}).catch(function(e){
console.log(e);
}); //delayedHello doesn't make it.
Promise.timeout(1200, delayedHello).then(function(data){
console.log(data);
}).catch(function(e){
console.log(e);
}); //delayedHello makes it.
Might be a little bit costly, because you are actually creating 3 promises instead of 2. I think it's clearer this way though.
可能有点贵,因为您实际上是在创建 3 个承诺而不是 2 个。我认为这样更清楚。
You might want to setup a promise instead of having the function construct it for you. This way you separate concerns and you are ultimately focused on racing your promise against a newly built promise that will reject at x
miliseconds.
您可能想要设置一个 promise,而不是让函数为您构造它。通过这种方式,您可以分离关注点,最终专注于将您的承诺与新构建的承诺进行竞争,该承诺将在x
几毫秒内拒绝。
Promise.timeout = function(timeout, promise){
return Promise.race([
promise,
new Promise(function(resolve, reject){
setTimeout(function() { reject('Timed out'); }, timeout);
})
]);
}
How to use:
如何使用:
var p = new Promise(function(resolve, reject){
setTimeout(function() { resolve('Hello'); }, 1000);
});
Promise.timeout(800, p); //will be rejected, as the promise takes at least 1 sec.
回答by Daniel Gruszczyk
This is slightly old question, but I stumbled upon this when I was looking how to timeout a promise.
Whilst all the answers are great, I found using bluebirdimplementation of Promises as the easiest way of handling timeouts:
这是一个有点老的问题,但是当我在寻找如何使承诺超时时偶然发现了这个问题。
虽然所有答案都很好,但我发现使用Promises 的bluebird实现是处理超时的最简单方法:
var Promise = require('bluebird');
var p = new Promise(function(reject, resolve) { /.../ });
p.timeout(3000) //make the promise timeout after 3000 milliseconds
.then(function(data) { /handle resolved promise/ })
.catch(Promise.TimeoutError, function(error) { /handle timeout error/ })
.catch(function(error) { /handle any other non-timeout errors/ });
As you can see this is so much less work than the other proposed solutions. I thought I will put it here to make it easier for people to find it :)
正如您所看到的,这比其他建议的解决方案工作量少得多。我想我会把它放在这里,让人们更容易找到它:)
Btw I am not by any means involved in bluebird project, just found this particular solution very neat.
顺便说一句,我绝不参与 bluebird 项目,只是发现这个特定的解决方案非常简洁。
回答by Drew Noakes
To add a timeout to any existing promise, you can use:
要将超时添加到任何现有的承诺,您可以使用:
const withTimeout = (millis, promise) => {
const timeout = new Promise((resolve, reject) =>
setTimeout(
() => reject(`Timed out after ${millis} ms.`),
millis));
return Promise.race([
promise,
timeout
]);
};
Then later:
后来:
await withTimeout(5000, doSomethingAsync());
回答by Drew Noakes
While the answers here are valid, you should not try to reinvent the wheel and just use one of the dozens available packages on NPM for the self-resolving promise.
虽然这里的答案是有效的,但您不应该尝试重新发明轮子,而只是使用 NPM 上的几十个可用包之一来实现自我解决承诺。
Here's one example from NPM:
const { TimeoutResolvePromise, TimeoutRejectPromise } = require('nodejs-promise-timeout');
const TIMEOUT_DELAY = 2000;
// This promise will reject after 2 seconds:
let promise1 = new TimeoutRejectPromise(TIMEOUT_DELAY, (resolve, reject) => {
// Do something useful here, then call resolve() or reject()
});
回答by vlio20
If your code is placed in a class you could use a decorator for that. You have such decorator in the utils-decorators(npm install --save utils-decorators
):
如果您的代码放在一个类中,您可以为此使用装饰器。您在utils-decorators( npm install --save utils-decorators
) 中有这样的装饰器:
import {timeout} from 'utils-decorators';
class SomeService {
@timeout(3000)
doSomeAsync(): Promise<any> {
....
}
}