为 promise.all 中的每个 promise 设置超时

新手上路,请多包涵

我能够成功执行 Promise.all,并优雅地处理解决和拒绝。然而,有些承诺会在几毫秒内完成,有些可能/可能需要一段时间。

我希望能够为 Promise.all 中的每个承诺设置超时,因此它可以尝试最多花费 5 秒。

 getData() {
    var that = this;
    var tableUrls = ['http://table-one.com','http://table-two.com'];
    var spoonUrls = ['http://spoon-one.com','http://spoon-two.com'];

    var tablePromises = that.createPromise(tableUrls);
    var spoonPromises = that.createPromise(spoonUrls);
    var responses = {};

    var getTableData = () => {
        var promise = new Promise((resolve, reject) => {
            Promise.all(tablePromises.map(that.rejectResolveHandle))
                .then((results) => {
                    responses.tables = results.filter(x => x.status === 'resolved');
                    resolve(responses);
                });
        });
        return promise;
    };

    var getSpoonData = () => {
        var promise = new Promise((resolve, reject) => {
            Promise.all(spoonPromises.map(that.rejectResolveHandle))
                .then((results) => {
                    responses.tables = results.filter(x => x.status === 'resolved');
                    resolve(responses);
                });
        });
        return promise;
    };

    return getTableData()
        .then(getSpoonData);
}

rejectResolveHandle() {
    return promise.then(function(v) {
        return {v:v, status: "resolved"};
    }, function(e) {
        return {e:e, status: "rejected"};
    });
}

createPromise(links) {
    var promises = [];
    angular.forEach(links, function (link) {
        var promise = that._$http({
            method: 'GET',
            url: link + '/my/end/point',
            responseType: 'json'
        });
        promises.push(promise);
    });

    return promises;
}

我尝试将超时添加到 createPromise() ,但这似乎不起作用。将超时设置为 300 毫秒,一些请求会持续 4 秒以上:

 createPromise(links) {
    var promises = [];
    angular.forEach(links, function (link) {
        var promise = that._$http({
            method: 'GET',
            url: link + '/my/end/point',
            responseType: 'json'
        });

        promise = new Promise((resolve) => {
            setTimeout(() => {
                resolve(promise);
            }, 300);
        });

        promises.push(promise);
    });

    return promises;
}

如果它能让事情变得更容易,我可以访问 Bluebird?

原文由 Oam Psy 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 2.3k
2 个回答

这是一个创建 Promise.raceAll() 函数的方案,它的工作方式有点像 Promise.all()Promise.race() 的组合 - 并且所有时间都承诺超时promise 在那之前没有解决,它将被短路以解决传入的值。这实质上将每个承诺放入一个带有计时器的 Promise.race() 中。如果计时器获胜,则使用默认值解决承诺。如果原始承诺获胜,则使用实际承诺结果解决。我们使用 Promise.race() 来解决第一个完成的问题(超时或原始承诺)。这是 Promise.race() 的经典用法(事实上,这是我真正用过的唯一实际用途)。

一个典型的例子是在接下来的 15 秒内让我得到你能得到的所有结果。任何超过 15 秒的结果,只需返回 null 给他们,不要等待他们。这是使这个概念起作用的代码:

 Promise.delay = function(t, val) {
    return new Promise(resolve => {
        setTimeout(resolve.bind(null, val), t);
    });
}

Promise.raceAll = function(promises, timeoutTime, timeoutVal) {
    return Promise.all(promises.map(p => {
        return Promise.race([p, Promise.delay(timeoutTime, timeoutVal)])
    }));
}

So, you use Promise.raceAll() like Promise.all() in that you pass it an array of promises, but you also pass it a timeoutTime and a timeoutValtimeoutTime 是承诺超时前等待的时间。 timeoutVal 是为任何超时的承诺放入结果数组的内容(通常它类似于 null 你可以很容易地将其识别为非真实结果)。


我不确定我是否完全了解您在特定代码中所做的事情,但这是使用上述内容的 links 代码:

 Promise.raceAll(links.map(link => {
    return that._$http({
        method: 'GET',
        url: link + '/my/end/point',
        responseType: 'json'
    });
}), 5000, null).then(results => {
    // process results here
    // any timed out values will be null
    // you can filter out the timed out results
    let final = results.filter(item => !!item);
}).catch(err => {
    // process any errors here
});

或者,如果您想确保 Promise.raceAll() 获得所有结果,即使某些承诺被拒绝,您也可以为每个承诺添加一个 .catch() 处理程序:

 Promise.raceAll(links.map(link => {
    return that._$http({
        method: 'GET',
        url: link + '/my/end/point',
        responseType: 'json'
    }).catch(err => {
        // don't let Promise.all() see a reject so it gets all results
        return null;
    });
}), 5000, null).then(results => {
    // process results here
    // any timed out values will be null
    // you can filter out the timed out or rejected results
    let final = results.filter(item => !!item);
}).catch(err => {
    // process any errors here
});

原文由 jfriend00 发布,翻译遵循 CC BY-SA 3.0 许可协议

您的尝试只是用超时承诺覆盖 promise ,这意味着 HTTP 承诺将被完全忽略。

这是您实际使用的相对较少的地方之一 new Promise *:创建承诺,根据 HTTP 调用解析或拒绝它, 然后 拒绝它(或解析它,但这看起来很奇怪)超时:

 createPromise(links) {
    return Promise.all(links.map(function(link) {
        return new Promise((resolve, reject) => {
            that._$http({
                method: 'GET',
                url: link + '/my/end/point',
                responseType: 'json'
            })
            .then(resolve)
            .catch(reject);
            setTimeout(() => {
                reject(/*...relevant value here...*/); // Seems like reject to me,
                                                       // but you could use resolve
                                                       // if you prefer
            }, 300);
        });
    }));
}

(使用 map 假设 links 是一个数组。)

请注意,一旦 resolvereject 被调用以获得承诺,随后对其中任何一个的调用将被完全忽略(没有错误或警告)。 (如果那不是真的,我们希望上面有一个标志来跟踪我们是否已经实现了承诺。)


\* (除非你有 setTimeout 的启用版本。如果你这样做,你会使用 Promise.race 就像 jfriend00 在 他们的回答 中所做的那样。)

原文由 T.J. Crowder 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题