Promise.some() 超时?

Promise.some() with a timeout?

用例 - 获取一堆 url 并缓存结果。那些快速完成的(比如 500 毫秒)被合并到这个过程中,任何比完成的时间更长的仍然完成并被保存到缓存中,所以下一次(在我们的应用程序中大约 10 分钟后)它们在缓存中。

我希望能够执行类似 Promise.someWithTimeout(promises, timeout) 的操作,其中结果是在超时到期之前完成的已解析值数组。 (Promise.some() 真的很接近它需要计数而不是超时)

类似

Promise.someWithTimeout([promises], <timeout value>)
.then((results) => {
  //stuff
})

每个承诺的样子

return memcache.getAsync(keyFromUrl(url))
.then((cacheRes) => {
  if(cacheRes) return cacheRes;
  //not in cache go get it
  return rp(url)
  .then((rpRes) => {
    //Save to cache but don't wait for it to complete
    memcache.setAsync(keyFromUrl(url), rpRes, 86400).catch((e) => { /*error logging*/ }) 
    return rpRes;
  })
})

我们的想法是,如果 url 返回得很快,我们就使用它,如果它需要更长的时间,我们就会放弃,但仍然会缓存结果,以便下次使用它。所以 rp() 超时会比 Promise.someWithTimeout() 超时长得多

有没有人写过一个库来做这个(我找不到),或者有更好的模式吗?我虽然考虑使用 Promise.all().timeout().finally() 和承诺将他们的结果存储到一个数组中,但由于我无法完全理解的原因,它感觉不对。

对于一次性编码,您可以启动两个单独的承诺链并使用一个标志来查看是否已经发生超时:

var timedOut = false;
// your regular code
Promise.all(...).then(function() {
    // cache the result, even if it came in after the timeout

    if (!timedOut) {
       // if we didn't time out, then show the response
    }
});

Promise.delay(1000).then(function() {
    timedOut = true;
    // do whatever you want to do on the timeout
});

如果您想将此功能打包到一个方法中,并且能够访问它超时的事实以及即使在超时后也能访问最终结果(例如,您可以缓存它们),那么您必须解决其中包含多个信息的问题。这是一种允许访问超时和最终数据的方法,方法是使用可以包含多个信息的对象进行解析。

Promise.someWithTimeout = function(promiseArray, cnt, timeout) {
    var pSome = Promise.some(promiseArray, cnt).then(function(results) {
        return {status: "done", results: results};
    });
    var pTimer = Promise.delay(cnt).then(function() {
        return {status: "timeout", promise: pSome}
    });
    return Promise.race([pSome, pTimer]);
}

// sample usage
Promise.someWithTimeout(arrayOfPromises, 3, 1000).then(function(result) {
    if (result.status === "timeout") {
        // we timed out
        // the result of the data is still coming when result.promise is resolved if you still want access to it later
        result.promise.then(function(results) {
            // can still get access to the eventual results here 
            // even though there was a timeout before they arrived
        }).catch(...);
    } else {
        // result.results is the array of results from Promise.some()
    }
});