了解promise.race()用法

时间:2017-09-23 05:39:34

标签: javascript jquery ajax es6-promise

据我所知,promise有两个选项:

好的,我知道promise.all()的作用。它并行运行promises,如果两者成功解析,.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()

6 个答案:

答案 0 :(得分:19)

如您所见,race()将返回首先解决或拒绝的promise实例:



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回调中调用回调。

答案 1 :(得分:5)

构建超时系统是一件事,其中:

  1. 请求/计算可能会被其他渠道取消
  2. 以后仍会使用它,但我们需要交互现在
  3. 对于第二个例子,人们可能会“立即”显示一个微调器,但如果速度足够快,则仍然默认为显示真实内容。尝试运行以下几次 - 注意至少一些控制台消息“立即”。通常可以将其附加到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();
    });

    a comment by 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。为了方便测试,它应该失败/成功大约一半/一半。

答案 2 :(得分:4)

我已将它用于请求批处理。为了长时间执行,我们不得不将成千上万条记录批量批量处理。我们可以并行完成,但不希望未决请求的数量失控。

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

答案 3 :(得分:2)

以下是了解promise.race()的使用的简单示例:

想象一下,您需要从服务器获取一些数据,如果数据加载时间太长(例如15秒),则需要显示错误。

您可以使用两个承诺调用promise.race(),第一个是您的ajax请求,第二个是简单的setTimeout(() => resolve("ERROR"), 15000)

答案 4 :(得分:1)

摘要:

Promise.race是一个JS内置函数,它接受Promise(例如Array)的迭代作为参数。然后,一旦解析或拒绝了迭代器中传递的一个承诺,此功能就会异步返回一个Promise。

示例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中传递一个Promises数组。这两个诺言都可以解决,但promise1可以更快地解决。因此,promise将使用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接受一个Promise的迭代,并基于该迭代中的第一个已解决或被拒绝的诺言返回一个Promise(带有相应的resolve()reject()值)。

答案 5 :(得分:0)

让我们采用Promise.race的示例解决方法,如下所示。

const race = (promises) => {
    return new Promise((resolve, reject) => {
        return promises.forEach(f => f.then(resolve).catch(reject));
    })
};

您可以看到race函数执行了所有的Promise,但是最先完成的人将使用包装器Promise进行解析/拒绝。