了解Promise.race()用法



据我所知,关于 promise 的两个选项:

  • promise.all()

  • promise.race()

好,我知道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()

如您所见,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))

使用race(),您只需要得到返回的承诺,您就不必关心race([])中的哪个承诺首先返回,

但是,如果没有race,就像您的示例一样,您需要关心哪个将首先返回,并在两个success回调中调用回调。

我已将其用于请求批处理。我们不得不批量批处理成千上万的记录,以长期执行。我们可以并行进行,但不希望待处理的请求数量失控。

竞赛让我们可以保持固定数量的并行承诺运行,并在完成时添加一个替换

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 });

这是构建超时系统的一部分,其中:

  1. 请求/计算可以由另一个频道取消
  2. 它仍将在以后使用,但是我们需要交互现在

对于第二个示例,一个人可能会"立即"显示旋转器,同时仍在默认情况下显示实际内容,如果它的快速速度足够快。尝试以下几次运行 - 注意至少某些控制台消息"立即"。通常可以将其附加到在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在超时击中时从未执行。它应该失败/成功大约一半/一半,以测试便利。

这是一个简单的例子,可以理解promise.race()的使用:

想象一下,您需要从服务器中获取一些数据,并且如果数据花费太长(例如15秒),则需要显示错误。

您将带有两个承诺调用Promise.race(),第一个是您的Ajax请求,第二个是简单的setTimeout(() => resolve("ERROR"), 15000)

摘要:

Promise.race是一种内置的函数,它接受诺言(例如 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.race中传递了一系列承诺。这两个承诺都解决了,但Promise1解决的速度更快。因此,承诺通过Promise1的值解决,即字符串'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.race将以'err'的值返回拒绝的承诺,这是Promise2拒绝的值。

要理解的关键点是,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解决/拒绝。

最新更新