NodeJS Délai d'attente d'une Promesse si n'a pas pu terminer dans le temps
Comment puis-je timeout une promesse après un certain temps? Je sais que Q A un temps mort de promesse, mais j'utilise les promesses natives de NodeJS et ils ne l'ont pas .fonction de délai.
Est-ce que je manque un ou son enveloppé différemment?
sinon, l'implémentation ci-dessous est-elle bonne pour ne pas aspirer la mémoire, et fonctionne-t-elle comme prévu?
je peux aussi le faire d'une manière ou d'une autre enveloppé globalement pour que je puisse l'utiliser pour chaque promesse que je crée, sans avoir à répéter le code setTimeout et 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);
});
}
Merci!
5 réponses
les promesses JavaScript natives n'ont pas de mécanisme de temporisation.
La question à propos de votre mise en œuvre serait probablement un meilleur ajustement pour http://codereview.stackexchange.com, mais quelques remarques:
vous ne fournissez pas un moyen de réellement faire quoi que ce soit dans la promesse, et
Il n'y a pas besoin de
clearTimeout
dans votresetTimeout
rappel, depuissetTimeout
horaires un one-off minuterie.Puisqu'une promesse ne peut pas être résolue/rejetée une fois qu'elle a été résolue/rejetée, vous n'avez pas besoin de cette vérification.
alors peut-être quelque chose dans ce sens:
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);
});
}
utilise comme ceci:
myPromise(2000, function(resolve, reject) {
// Real work is here
});
(Ou voulez qu'il soit un peu plus compliqué, voir mise à jour sous la ligne ci-dessous.)
je serais légèrement préoccupé par le fait que la sémantique sont légèrement différents (Non new
, alors que vous ne l'utilisez new
Promise
constructeur), donc vous pouvez ajuster cela.
l'autre problème, bien sûr, est que la plupart du temps, vous ne voulez pas construire de nouvelles promesses, et donc ne pouvait pas utiliser ce qui précède. La plupart du temps, vous avez déjà une promesse (le résultat d'un précédent then
appel, etc.). Mais pour les situations où vous construisez vraiment une nouvelle promesse, vous pourriez utiliser quelque chose comme ce qui précède.
Vous peut faire face à la new
chose subclassing 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);
}
});
}
}
Utilisation:
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>
les deux appelleront reject
quand le timer expire même si le callback appelle resolve
ou reject
en premier. C'est très bien, l'état d'une promesse ne peut pas être modifié une fois qu'elle est définie, et le spec définit les appels à resolve
ou reject
sur une promesse de déjà réglé comme un rien qui ne soulève pas d'erreur.
Mais si cela vous dérange, vous pouvez envelopper resolve
et reject
. Ici myPromise
fait de cette manière:
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);
}
);
});
}
vous pouvez le faire de 18 façons différentes, mais le concept de base est que le resolve
et reject
nous transmettons l'exécuteur de la promesse que nous recevons sont des enveloppes qui libèrent le minuteur.
Mais, qui crée des fonctions et des appels de fonctions supplémentaires dont vous n'avez pas besoin. La spec est clair sur ce que les fonctions de résolution font lorsque la promesse est déjà résolue; ils quittent assez tôt.
alors que peut-être il n'y a aucun soutien pour un temps d'arrêt de promesse, vous pourriez course promesses:
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);
});
générique Promise.timeout
:
Promise.timeout = function(timeout, cb){
return Promise.race([
new Promise(cb),
new Promise(function(resolve, reject){
setTimeout(function() { reject('Timed out'); }, timeout);
})
]);
}
Exemple:
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.
pourrait être un peu coûteux, parce que vous créez en fait 3 promesses au lieu de 2. Je pense que c'est plus clair de cette façon.
vous pourriez vouloir configurer une promesse au lieu d'avoir la fonction la construire pour vous. De cette façon, vous séparer préoccupations et vous êtes finalement concentré sur la course de votre promesse contre une promesse nouvellement construit qui rejettera à x
millisecondes.
Promise.timeout = function(timeout, promise){
return Promise.race([
promise,
new Promise(function(resolve, reject){
setTimeout(function() { reject('Timed out'); }, timeout);
})
]);
}
utilisation:
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.
c'est une question un peu vieille, mais je suis tombé sur cela quand je regardais comment timeout une promesse.
Alors que toutes les réponses sont bonnes, j'ai trouvé en utilisant merle-bleu la mise en oeuvre des promesses comme moyen le plus facile de gestion des timeouts:
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/ });
Comme vous pouvez le voir c'est donc beaucoup moins de travail que les autres solutions proposées. J'ai pensé que je vais le mettre ici pour le rendre plus facile pour les gens à trouver :)
Btw, je ne suis pas en toute moyens impliqués dans le projet bluebird, vient de trouver cette solution particulière très soignée.
Pour ajouter un délai à toute promesse, vous pouvez utiliser:
const withTimeout = (millis, promise) => {
const timeout = new Promise((resolve, reject) =>
setTimeout(
() => reject(`Timed out after ${millis} ms.`),
millis));
return Promise.race([
promise,
timeout
]);
};
Puis, plus tard:
await withTimeout(5000, doSomethingAsync());
alors que les réponses ici sont valables, vous ne devriez pas essayer de réinventer la roue et utiliser un des paquets disponibles sur NPM pour la promesse auto-résolutive.
Voici un exemple de 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()
});