web-dev-qa-db-fra.com

Comprendre l'utilisation de promise.race ()

Pour autant que je sache, il existe deux options concernant promise :

Ok, je sais ce que fait promise.all(). Il exécute des promesses en parallèle et .then Vous donne les valeurs si les deux sont résolus avec succès. Voici un exemple:

Promise.all([
  $.ajax({ url: 'test1.php' }),
  $.ajax({ url: 'test2.php' })
])
.then(([res1, res2]) => {
  // Both requests resolved
})
.catch(error => {
  // Something went wrong
});

Mais je ne comprends pas ce que promise.race() est censé faire exactement? Dans un autre Word, quelle est la différence avec ne pas l'utiliser? Supposons ceci:

$.ajax({
    url: 'test1.php',
    async: true,
    success: function (data) {
        // This request resolved
    }
});

$.ajax({
    url: 'test2.php',
    async: true,
    success: function (data) {
        // This request resolved
    }
});

Voir? Je n'ai pas utilisé promise.race() et il se comporte comme promise.race(). Quoi qu'il en soit, existe-t-il un exemple simple et propre pour me montrer quand dois-je utiliser exactement promise.race()?

9
Martin AJ

Comme vous le voyez, la race() renverra l'instance de promesse qui est d'abord résolue ou rejetée:

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

Pour une scène à utiliser - peut-être que vous voulez limiter le temps de coût d'une demande:

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

Avec la race() vous avez juste besoin d'obtenir la promesse retournée, vous n'avez pas besoin de savoir laquelle des promesses de la race([]) a d'abord retournée,

Cependant, sans race, tout comme votre exemple, vous devez vous soucier de celui qui sera d'abord renvoyé et appeler le rappel dans les deux success callback.

24
JiangangXiong

C'est une pièce pour construire un système de timeout, où:

  1. la demande/le calcul peut être annulé par un autre canal
  2. il sera toujours utilisé plus tard, mais nous avons besoin d'une interaction maintenant .

Pour un exemple de la seconde, on peut montrer un spinner "instantanément" tout en continuant par défaut à afficher du contenu réel s'il arrive assez rapidement. Essayez d'exécuter la commande ci-dessous plusieurs fois - notez qu'au moins certains messages de la console arrivent "instantanément". Cela peut normalement être attaché pour effectuer des opérations sur une interface utilisateur.

La clé à noter est - le résultat de Promise.race est beaucoup moins important que les effets secondaires (cependant, c'est alors une odeur de code).

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

Inspiration crédit à n commentaire de captainkovalsky .

Un exemple du premier:

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

Vous pouvez voir sur celui-ci que le délai d'attente console.log n'est jamais exécuté lorsque le délai expire en premier. Il devrait échouer/réussir environ la moitié/la moitié, pour des raisons de commodité de test.

5
Iiridayn

Je l'ai utilisé pour le traitement par lots des demandes. Nous avons dû regrouper des dizaines de milliers d'enregistrements en lots pour une exécution de longue durée. Nous pourrions le faire en parallèle, mais nous ne voulions pas que le nombre de demandes en attente devienne incontrôlable.

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

Voici un exemple simple pour comprendre l'utilisation de promise.race():

Imaginez que vous ayez besoin de récupérer des données à partir d'un serveur et si les données prennent trop de temps à charger (disons 15 secondes), vous souhaitez afficher une erreur.

Vous appelleriez promise.race () avec deux promesses, la première étant votre requête ajax et la seconde étant un simple setTimeout(() => resolve("ERROR"), 15000)

3
thelmuxkriovar

Prenons un exemple de solution de contournement de Promise.race comme ci-dessous.

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

Vous pouvez voir que la fonction race exécute toutes les promesses, mais celui qui termine en premier résoudra/rejetera avec le wrapper Promise.

1
rab

Résumé:

Promise.race Est une fonction JS intégrée qui accepte comme argument un itérable de promesses (par exemple Array). Cette fonction alors de manière asynchrone renvoie une promesse dès qu'une des promesses passées dans l'itérable est soit résolue, soit rejetée.

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

Dans cet exemple, un tableau de promesses est d'abord passé dans Promise.race. Les deux promesses se résolvent mais la promesse 1 se résout plus rapidement. Par conséquent, la promesse est résolue avec la valeur de promise1, qui est la chaîne 'Promise-one'.

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

Dans ce deuxième exemple, la deuxième promesse rejette plus rapidement que la première promesse ne peut résoudre. Par conséquent, Promise.race Renverra une promesse rejetée avec la valeur de 'err' Qui était la valeur avec laquelle Promise2 a rejeté.

Le point clé à comprendre est que Promice.race Prend un itérable de promesses et renvoie une promesse basée sur la première promesse résolue ou rejetée dans cet itérable (avec le resolve() ou reject() correspondant valeurs).

1
Willem van der Veen