关于javascript:在promise中使函数超时的最佳一般做法是什么

What is the best general practice to timeout a function in promise

通过超时使函数调用

我已经看到许多资源提供了类似的示例,这些示例使用Promise.race在给定的时间段内使函数调用超时。这是一个非常好的示例,说明如何在实践中使用Promise.race。下面是一些示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
function doWithinInterval(func, timeout) {
    var promiseTimeout = new Promise(function (fulfill, reject) {
       // Rejects as soon as the timeout kicks in
       setTimeout(reject, timeout);
    });
    var promiseFunc = new Promise(function (fulfill, reject) {
        var result = func(); // Function that may take long to finish
        // Fulfills when the given function finishes
        fulfill(result);
    });

    return Promise.race([promiseTimeout, promiseFunc]);
}

上面使用Promise.race的简单方法是在func完成之前在超时开始后立即拒绝承诺。否则,一旦func函数在超时间隔之前完成,项目就会完成。

听起来不错,易于使用。

但是,这是在Promise中使用超时的最佳实践吗?

当然,如果我们想使用Promises对函数调用设置超时,则可以采用上述方法。行动似乎仍然是一个很好的希望。但是,这被认为是在Promise中使用超时的一种好习惯吗?如果没有,使用它的缺点是什么?

我一直在寻找替代方法,但找不到本机的Promise方法。

相反,某些外部Promise库提供了timeout功能,如下所示:

  • 蓝鸟耗材.timeout()

  • WinJS也提供.timeout()

  • Q还附带了.timeout()

但是,Promise.timeout()似乎不是标准ECMAScript 6 API的一部分(如果我错了,请纠正我)。有没有推荐的方法来通过ES6 Promises本地处理超时?


这取决于您所说的超时。

如果您希望该功能停止,则不会。

如果您只想停止等待,则可以(在ES6中快速启动):

1
2
3
4
var wait = ms => new Promise(resolve => setTimeout(resolve, ms));
var timeout = (p, ms) => Promise.race([p, wait(ms).then(() => {
    throw new Error("Timeout after" + ms +" ms");
})]);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var wait = ms => new Promise(resolve => setTimeout(resolve, ms));
var timeout = (p, ms) => Promise.race([p, wait(ms).then(() => {
  throw new Error("Timeout after" + ms +" ms");
})]);

// Example:

var log = msg => div.innerHTML +="<p>" + msg +"</p>";
var failed = e => log(e.toString() +", line" + e.lineNumber);

log("Waiting 5 seconds...");
timeout(wait(5000), 2000)
.then(() => log("...Done."))
.catch(failed);
1
 

如果要取消操作(使其停止),则希望该操作附带API来取消它,并且应该使用它,因为ES6 Promise不是控制界面。

可取消承诺是ES6中一个有争议的主题,但是提到的某些库确实提供了这个概念。


原生Promise.race方法在实际承诺完成后不会清除超时承诺的计时器,因此该过程将等待,直到超时承诺也完成。这意味着如果将超时设置为1h并且我们的承诺在1分钟后完成,则该过程将等待59分钟后退出。

改为使用此方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
export function race({promise, timeout, error}) {
  let timer = null;

  return Promise.race([
    new Promise((resolve, reject) => {
      timer = setTimeout(reject, timeout, error);
      return timer;
    }),
    promise.then((value) => {
      clearTimeout(timer);
      return value;
    })
  ]);
}