了解promise.race()用法

Mar*_* AJ 9 javascript ajax jquery 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
});
Run Code Online (Sandbox Code Playgroud)

但我不明白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
    }
});
Run Code Online (Sandbox Code Playgroud)

看到?我没有使用promise.race()它,它表现得像promise.race().无论如何,有什么简单而干净的例子可以告诉我何时应该使用promise.race()

Jia*_*ong 24

如您所见,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
});
Run Code Online (Sandbox Code Playgroud)

对于要使用的场景,您可能希望限制请求的成本时间:

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))
Run Code Online (Sandbox Code Playgroud)

随着race()你只需要获取返回的承诺,你不必在意在其承诺的一个race([])先返回,

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

  • 但是,`fetch`方法调用会继续,但只要最终返回,输出就会被丢弃.超时应该取消挂起的请求. (8认同)
  • @Dominic,区别在于“Promise.race”会更快,原因如下:一旦您提供的任何承诺“解决”,“Promise.race”就会解决,无论它们是履行还是拒绝。一旦您提供的任何 Promise _fulfilled_ 或它们全部被拒绝,`Promise.any` 就会被解决,在这种情况下,它会因 AggregateError 被拒绝。因此,在“全部被拒绝”的情况下,“Promise.any”会变慢,因为它会等到所有承诺都被拒绝。区别不大,但仍然如此。 (4认同)

Dan*_*nyM 6

这是一个简单的例子来理解使用promise.race()

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

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

  • 为什么不在网络调用中使用超时设置? (4认同)

Wil*_*een 6

概括:

Promise.race是一个 JS 内置函数,它接受可迭代的 Promise(例如Array)作为参数。然后,一旦在可迭代对象中传递的 Promise 中的一个被解决或被拒绝,此函数就会异步返回一个 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
});
Run Code Online (Sandbox Code Playgroud)

在这个例子中,首先传入了一个 Promises 数组Promise.race。两个承诺都解决了,但 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);
});
Run Code Online (Sandbox Code Playgroud)

在第二个示例中,第二个 Promise 的拒绝速度比第一个 Promise 可以解决的速度快。因此Promise.race将返回一个被拒绝的承诺,其值'err'是 Promise2 拒绝的值。

要理解的关键点是它Promice.race接受一个可迭代的 Promise 并根据该可迭代中的第一个已解决或被拒绝的承诺(具有相应的resolve()reject()值)返回一个 Promise 。


Iir*_*ayn 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();
});
Run Code Online (Sandbox Code Playgroud)

灵感来自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);
Run Code Online (Sandbox Code Playgroud)

从这一点可以看出,console.log当超时首先命中时,永远不会执行超时.为了方便测试,它应该失败/成功大约一半/一半.


Chr*_*erJ 5

我已将其用于请求批处理。为了长期运行,我们必须将成千上万的记录分批处理。我们可以并行执行此操作,但不希望待处理的请求数量失控。

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 });
Run Code Online (Sandbox Code Playgroud)