为 promise.all 内的每个承诺设置超时
Setting a timeout for each promise within a promise.all
我能够成功执行 Promise.all,并优雅地处理解决和拒绝。但是,有些承诺会在几毫秒内完成,有些 can/could 需要一段时间。
我希望能够为 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,如果它能让事情变得更容易?
您的尝试只是用超时承诺覆盖 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);
});
}));
}
(假设 links
是一个数组,使用 map
。)
请注意,一旦为承诺调用 resolve
或 reject
,后续对其中任何一个的调用将被完全忽略(没有错误或警告)。 (如果那不是真的,我们希望上面有一个标志来跟踪我们是否已经兑现承诺。)
* (除非你有 setTimeout
的 promise 启用版本。如果你这样做,你会像 jfriend00 在 中那样使用 Promise.race
。 )
这是一个创建 Promise.raceAll()
函数的方案,它的工作方式有点像 Promise.all()
和 Promise.race()
的组合,其中承诺都有超时时间和值,因此如果promise 在那之前没有解决,它将被短路以解决传入的值。这实质上是将每个承诺放入带有计时器的 Promise.race()
中。如果计时器获胜,则使用默认值解决承诺。如果原始承诺获胜,则使用实际承诺结果解决。我们使用 Promise.race()
来解决第一个完成的问题(超时或原始承诺)。这是 Promise.race()
的经典用法(实际上是我真正用过的唯一实际用途)。
一个典型的例子是在接下来的 15 秒内让我得到所有你能得到的结果。任何超过 15 秒的结果,只需 return 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()
一样,因为您向它传递了一组承诺,但您还向它传递了一个 timeoutTime
和一个 timeoutVal
。 timeoutTime
是在承诺超时之前等待的时间。 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
});
一个解决方案可能是将 Promise.all
的参数数组中的每个承诺转换为超时承诺(比如使用 .map
)。超时函数可以根据应用程序需要解析或拒绝提供的参数承诺。
在超时后使用唯一值解析承诺允许在提供给 Promise.all.then( handler)
的结果数组中识别它
此示例使用辅助函数 timedPromise
将承诺转换为 已解决 承诺。不包括将承诺数组映射到超时承诺数组。
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)
);
我能够成功执行 Promise.all,并优雅地处理解决和拒绝。但是,有些承诺会在几毫秒内完成,有些 can/could 需要一段时间。
我希望能够为 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,如果它能让事情变得更容易?
您的尝试只是用超时承诺覆盖 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);
});
}));
}
(假设 links
是一个数组,使用 map
。)
请注意,一旦为承诺调用 resolve
或 reject
,后续对其中任何一个的调用将被完全忽略(没有错误或警告)。 (如果那不是真的,我们希望上面有一个标志来跟踪我们是否已经兑现承诺。)
* (除非你有 setTimeout
的 promise 启用版本。如果你这样做,你会像 jfriend00 在 Promise.race
。 )
这是一个创建 Promise.raceAll()
函数的方案,它的工作方式有点像 Promise.all()
和 Promise.race()
的组合,其中承诺都有超时时间和值,因此如果promise 在那之前没有解决,它将被短路以解决传入的值。这实质上是将每个承诺放入带有计时器的 Promise.race()
中。如果计时器获胜,则使用默认值解决承诺。如果原始承诺获胜,则使用实际承诺结果解决。我们使用 Promise.race()
来解决第一个完成的问题(超时或原始承诺)。这是 Promise.race()
的经典用法(实际上是我真正用过的唯一实际用途)。
一个典型的例子是在接下来的 15 秒内让我得到所有你能得到的结果。任何超过 15 秒的结果,只需 return 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()
一样,因为您向它传递了一组承诺,但您还向它传递了一个 timeoutTime
和一个 timeoutVal
。 timeoutTime
是在承诺超时之前等待的时间。 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
});
一个解决方案可能是将 Promise.all
的参数数组中的每个承诺转换为超时承诺(比如使用 .map
)。超时函数可以根据应用程序需要解析或拒绝提供的参数承诺。
在超时后使用唯一值解析承诺允许在提供给 Promise.all.then( handler)
此示例使用辅助函数 timedPromise
将承诺转换为 已解决 承诺。不包括将承诺数组映射到超时承诺数组。
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)
);