提问人:Martin AJ 提问时间:9/23/2017 最后编辑:KalyanLahkarMartin AJ 更新时间:5/17/2020 访问量:34365
了解 promise.race() 用法
Understanding promise.race() usage
问:
据我所知,关于承诺
有两种选择:
好的,我知道有什么作用。它并行运行 promise,如果两者都成功解析,则为您提供值。下面是一个示例:promise.all()
.then
Promise.all([
$.ajax({ url: 'test1.php' }),
$.ajax({ url: 'test2.php' })
])
.then(([res1, res2]) => {
// Both requests resolved
})
.catch(error => {
// Something went wrong
});
但我不明白到底应该做什么?换句话说,不使用它有什么区别?假设:promise.race()
$.ajax({
url: 'test1.php',
async: true,
success: function (data) {
// This request resolved
}
});
$.ajax({
url: 'test2.php',
async: true,
success: function (data) {
// This request resolved
}
});
看?我没有使用过,它的行为就像.无论如何,有没有简单干净的例子可以告诉我什么时候应该使用?promise.race()
promise.race()
promise.race()
答:
如您所见,将返回首先解析或拒绝的 promise 实例:race()
var p1 = new Promise(function(resolve, reject) {
setTimeout(resolve, 500, 'one');
});
var p2 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, 'two');
});
Promise.race([p1, p2]).then(function(value) {
console.log(value); // "two"
// Both resolve, but p2 is faster
});
对于要使用的场景,也许您想限制请求的成本时间:
var p = Promise.race([
fetch('/resource-that-may-take-a-while'),
new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('request timeout')), 5000)
})
])
p.then(response => console.log(response))
p.catch(error => console.log(error))
有了你只需要得到返回的 promise,你就不需要关心 first 返回的 promise 中的哪一个,race()
race([])
但是,如果没有 ,就像您的示例一样,您需要关心哪个会首先返回,并在两个回调中调用回调。race
success
评论
fetch
Promise.any
Promise.race
Promise.race
Promise.any
Promise.any
这里有一个简单的例子来理解 的用法:promise.race()
想象一下,您需要从服务器获取一些数据,如果数据加载时间过长(例如 15 秒),则要显示错误。
你可以用两个 promise 调用 promise.race(),第一个是你的 ajax 请求,第二个是一个简单的setTimeout(() => resolve("ERROR"), 15000)
评论
timeout
这是构建超时系统的一部分,其中:
- 请求/计算可能会被其他通道取消
- 它以后仍然会使用,但我们现在需要交互。
对于第二个示例,可以“立即”显示微调器,同时如果输入速度足够快,则仍默认显示真实内容。尝试运行以下几次 - 请注意,至少有一些控制台消息“立即”出现。这通常是为了在 UI 上执行操作而附加的。
需要注意的关键是 - 结果远不如副作用重要(尽管这是一种代码气味)。Promise.race
// 300 ms _feels_ "instant", and flickers are bad
function getUserInfo(user) {
return new Promise((resolve, reject) => {
// had it at 1500 to be more true-to-life, but 900 is better for testing
setTimeout(() => resolve("user data!"), Math.floor(900*Math.random()));
});
}
function showUserInfo(user) {
return getUserInfo().then(info => {
console.log("user info:", info);
return true;
});
}
function showSpinner() {
console.log("please wait...")
}
function timeout(delay, result) {
return new Promise(resolve => {
setTimeout(() => resolve(result), delay);
});
}
Promise.race([showUserInfo(), timeout(300)]).then(displayed => {
if (!displayed) showSpinner();
});
灵感来源于captainkovalsky的评论。
第一个例子:
function timeout(delay) {
let cancel;
const wait = new Promise(resolve => {
const timer = setTimeout(() => resolve(false), delay);
cancel = () => {
clearTimeout(timer);
resolve(true);
};
});
wait.cancel = cancel;
return wait;
}
function doWork() {
const workFactor = Math.floor(600*Math.random());
const work = timeout(workFactor);
const result = work.then(canceled => {
if (canceled)
console.log('Work canceled');
else
console.log('Work done in', workFactor, 'ms');
return !canceled;
});
result.cancel = work.cancel;
return result;
}
function attemptWork() {
const work = doWork();
return Promise.race([work, timeout(300)])
.then(done => {
if (!done)
work.cancel();
return (done ? 'Work complete!' : 'I gave up');
});
}
attemptWork().then(console.log);
从中可以看出,当超时首先命中时,超时永远不会执行。为了方便测试,它应该失败/成功大约一半/一半。console.log
评论
timeout
catch
setSpinner()
var delayedSpinner = (ms) => new Promise((resolve, reject) => setTimeout(resolve, ms, 'please wait...')); Promise.race([showUserInfo(), delayedSpinner(300)])
showUserInfo
undefined
我已将它用于请求批处理。我们必须将数以万计的记录批处理成批,以便长时间运行。我们可以并行执行此操作,但不希望待处理请求的数量失控。
Race 允许我们保持固定数量的并行承诺运行,并在完成时添加一个替换
const _ = require('lodash')
async function batchRequests(options) {
let query = { offset: 0, limit: options.limit };
do {
batch = await model.findAll(query);
query.offset += options.limit;
if (batch.length) {
const promise = doLongRequestForBatch(batch).then(() => {
// Once complete, pop this promise from our array
// so that we know we can add another batch in its place
_.remove(promises, p => p === promise);
});
promises.push(promise);
// Once we hit our concurrency limit, wait for at least one promise to
// resolve before continuing to batch off requests
if (promises.length >= options.concurrentBatches) {
await Promise.race(promises);
}
}
} while (batch.length);
// Wait for remaining batches to finish
return Promise.all(promises);
}
batchRequests({ limit: 100, concurrentBatches: 5 });
评论
_.remove()
for (let i = 0; i < promises.length; i++) { if (promises[i] === promise) { promises.splice(i, 1); break; } }
总结:
Promise.race
是一个 JS 内置函数,它接受 Promise 的可迭代对象(例如 )作为参数。然后,一旦在可迭代对象中传递的 Promise 中的一个被解析或拒绝,此函数就会异步返回 Promise。Array
示例 1:
var promise1 = new Promise((resolve, reject) => {
setTimeout(() => resolve('Promise-one'), 500);
});
var promise2 = new Promise((resolve, reject) => {
setTimeout(() => resolve('Promise-two'), 100);
});
Promise.race([promise1, promise2]).then((value) => {
console.log(value);
// Both resolve, but promise2 is faster than promise 1
});
在此示例中,首先传入一个 Promise 数组。两个 promise 都解析,但 promise1 解析得更快。因此,promise 使用 promise1 的值进行解析,该值是字符串 。Promise.race
'Promise-one'
示例 2:
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => resolve('succes'), 2000);
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => reject('err'), 1000);
});
Promise.race([promise1, promise2])
.then((value) => {
console.log(value);
}).catch((value) => {
console.log('error: ' + value);
});
在第二个示例中,第二个 promise 的拒绝速度比第一个 promise 的解析速度快。因此,将返回一个被拒绝的 promise,其值是 Promise2 拒绝的值。Promise.race
'err'
要理解的关键点是,获取 Promise 的可迭代对象,并根据该可迭代对象中的第一个已解决或拒绝的 promise(具有相应的 or 值)返回一个 Promise。Promice.race
resolve()
reject()
评论
让我们采取如下的解决方法示例。Promise.race
const race = (promises) => {
return new Promise((resolve, reject) => {
return promises.forEach(f => f.then(resolve).catch(reject));
})
};
你可以看到函数执行了所有的承诺,但谁先完成,谁就会用包装器解决/拒绝。race
Promise
评论
Promise.allSettled