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

时间:2018-02-02 07:26:04

标签: javascript angularjs angular-promise es6-promise bluebird

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

我希望能够为Promise.all中的每个promise设置超时,因此它可以尝试最多花费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吗?

3 个答案:

答案 0 :(得分:8)

这是一个创建Promise.raceAll()函数的方案,该函数类似于Promise.all()Promise.race()的组合,其中所有承诺都具有超时时间和值,以便承诺在此之前没有解决,它将被短路以解决传入的价值。这基本上将每个承诺放入带有计时器的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)])
    }));
}

因此,您使用Promise.raceAll()之类的Promise.all(),因为您传递了一系列承诺,但您也传递了timeoutTimetimeoutValtimeoutTime是在超时承诺之前等待的时间。对于任何超时的承诺,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
});

答案 1 :(得分:2)

您的尝试只会使用超时承诺覆盖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版本。如果您这样做,则可以像their answer中的jfriend00一样使用Promise.race。)< / em>的

答案 2 :(得分:0)

一种解决方案可能是将Promise.all的参数数组中的每个promise转换为超时的promise(比如使用.map)。超时函数可以根据应用程序需要解析或拒绝提供的参数promise。

使用唯一值在超时后解析承诺允许在提供给Promise.all.then( handler)

的结果数组中标识它

此示例着眼于使用辅助函数timedPromise将承诺转换为已解决的承诺。不包括将promise数组映射到超时promise的数组。

&#13;
&#13;
const timedOutValue = new Error( "promise timeout"); // unique value
const timedPromise = (promise, timeout) => {
    return new Promise( (resolve, reject) => {
      promise.then(resolve, reject);
      setTimeout( resolve, timeout, timedOutValue);  // could have used reject
    });
};

var pendingPromise = new Promise( (resolve,reject) => null);  // never settles 
timedPromise(pendingPromise)
.then(
    data=> console.log( "pendingPromise resolved with %s",
       data === timedOutValue ? "timedOutValue" : data),
    reason=> console.log( "pendingPromise rejected with %s",
       reason === timedOutValue ? "timedOutValue" : reason)
);
 
&#13;
&#13;
&#13;