一定時間後に約束をタイムアウトするにはどうすればよいですか? Qにはプロミスタイムアウトがあることは知っていますが、ネイティブNodeJSプロミスを使用しており、.timeout関数はありません。
不足しているものがあるか、ラップが異なっていますか?
または、以下の実装は、メモリを吸い上げないという意味で優れており、実際に期待どおりに機能していますか?
また、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);
});
}
ありがとう!
ネイティブJavaScriptプロミスにはタイムアウトメカニズムがありません。
あなたの実装に関する質問は、おそらく http://codereview.stackexchange.com に適していますが、いくつかの注意事項があります。
約束の中で実際に何かをする手段を提供していない、そして
clearTimeout
が一時タイマーをスケジュールするため、setTimeout
コールバック内にsetTimeout
は必要ありません。
約束が解決/拒否されると、約束は解決/拒否できないため、そのチェックは必要ありません。
おそらくこれらの線に沿って何か:
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);
});
}
このように使用します:
myPromise(2000, function(resolve, reject) {
// Real work is here
});
(または(mayもう少し複雑にしたい場合は、以下の行の更新を参照してください。)
セマンティクスがわずかに異なる(new
なし、new
コンストラクターでPromise
を使用する)ことを少し心配するので、調整するかもしれません。
もちろん、もう1つの問題は、ほとんどの場合、新しいプロミスを作成したくないため、上記を使用できないことです。ほとんどの場合、すでに約束があります(以前のthen
呼び出しの結果など)。しかし、新しい約束を本当に構築している状況では、上記のようなものを使用できます。
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);
}
});
}
}
使用法:
let p = new MyPromise(300, function(resolve, reject) {
// ...
});
p.then(result => {
})
.catch(error => {
});
ライブの例:
// 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>
コールバックが最初にreject
またはresolve
を呼び出しても、タイマーが期限切れになると、両方ともreject
を呼び出します。それは問題ありません。一度設定すると、約束の確定状態は変更できません。また、仕様は、エラーを発生させないこととして既に確定している約束のresolve
またはreject
への呼び出しを定義します。 。
しかし、気になる場合は、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);
}
);
});
}
これを約18の異なる方法でスピンできますが、基本的な概念は、受け取ったpromise executorを渡すresolve
とreject
はタイマーをクリアするラッパーであるということです。
しかし、それはあなたが必要としない関数と余分な関数呼び出しを作成します。 仕様は明確です 約束がすでに解決されたときに解決関数が何をするかについて;彼らはかなり早く終了しました。
約束のタイムアウトはサポートされていないかもしれませんが、約束を競うことができます。
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);
});
汎用Promise.timeout
:
Promise.timeout = function(timeout, cb){
return Promise.race([
new Promise(cb),
new Promise(function(resolve, reject){
setTimeout(function() { reject('Timed out'); }, timeout);
})
]);
}
例:
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.
実際には2つではなく3つのプロミスを作成しているため、少しコストがかかる場合があります。
関数がそれを構築する代わりに、promiseをセットアップしたい場合があります。このようにして、懸念を分離し、最終的に、x
ミリ秒で拒否される新たに作成されたプロミスに対してプロミスを競うことに集中します。
Promise.timeout = function(timeout, promise){
return Promise.race([
promise,
new Promise(function(resolve, reject){
setTimeout(function() { reject('Timed out'); }, timeout);
})
]);
}
使い方:
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.
既存のプロミスにタイムアウトを追加するには、次を使用できます。
const withTimeout = (millis, promise) => {
const timeout = new Promise((resolve, reject) =>
setTimeout(
() => reject(`Timed out after ${millis} ms.`),
millis));
return Promise.race([
promise,
timeout
]);
};
それじゃあ、後でね:
await withTimeout(5000, doSomethingAsync());
これは少し古い質問ですが、約束をタイムアウトする方法を探していたときに、私はこれにつまずきました。
すべての答えは素晴らしいですが、 bluebird Promiseの実装を タイムアウトの処理 の最も簡単な方法として使用していることがわかりました。
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/ });
ご覧のとおり、これは他の提案されたソリューションよりもはるかに少ない作業です。人々が見つけやすくするためにここに置くと思った:)
ところで、私はブルーバードプロジェクトに関与しているわけではありません。この特定のソリューションは非常にすっきりしています。
ここでの答えは有効ですが、車輪の再発明を試みて、NPMで利用可能な数十のパッケージの1つを使用して、自己解決の約束をするべきではありません。
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()
});