web-dev-qa-db-fra.com

Combinaison de la fonction asynchrone + wait + setTimeout

J'essaie d'utiliser les nouvelles fonctionnalités asynchrones et j'espère que la résolution de mon problème aidera les autres à l'avenir. Ceci est mon code qui fonctionne:

  async function asyncGenerator() {
    // other code
    while (goOn) {
      // other code
      var fileList = await listFiles(nextPageToken);
      var parents = await requestParents(fileList);
      // other code
    }
    // other code
  }

  function listFiles(token) {
    return gapi.client.drive.files.list({
      'maxResults': sizeResults,
      'pageToken': token,
      'q': query
    });
  }

Le problème est que ma boucle while s'exécute trop rapidement et que le script envoie trop de requêtes par seconde à l'API Google. Par conséquent, j'aimerais créer une fonction de veille qui retarde la demande. Ainsi, je pourrais aussi utiliser cette fonction pour retarder d’autres demandes. S'il y a un autre moyen de retarder la demande, s'il vous plaît faites le moi savoir.

Quoi qu'il en soit, c'est mon nouveau code qui ne fonctionne pas. La réponse de la demande est renvoyée à la fonction asynchrone anonyme dans setTimeout, mais je ne sais tout simplement pas comment je peux renvoyer la réponse à la fonction sleep, respectivement. à la fonction asyncGenerator initiale.

  async function asyncGenerator() {
    // other code
    while (goOn) {
      // other code
      var fileList = await sleep(listFiles, nextPageToken);
      var parents = await requestParents(fileList);
      // other code
    }
    // other code
  }

  function listFiles(token) {
    return gapi.client.drive.files.list({
      'maxResults': sizeResults,
      'pageToken': token,
      'q': query
    });
  }

  async function sleep(fn, par) {
    return await setTimeout(async function() {
      await fn(par);
    }, 3000, fn, par);
  }

J'ai déjà essayé quelques options: stocker la réponse dans une variable globale et la renvoyer depuis la fonction de veille, rappeler dans la fonction anonyme, etc.

188
JShinigami

Votre fonction sleep ne fonctionne pas car setTimeout ne renvoie pas (encore?) Une promesse qui pourrait être awaited. Vous devrez le promettre manuellement:

function timeout(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}
async function sleep(fn, ...args) {
    await timeout(3000);
    return fn(...args);
}

Au fait, pour ralentir votre boucle, vous ne voudrez probablement pas utiliser une fonction sleep qui prend un rappel et le diffère comme ceci. Je recommanderais plutôt de faire quelque chose comme

while (goOn) {
  // other code
  var [parents] = await Promise.all([
      listFiles(nextPageToken).then(requestParents),
      timeout(5000)
  ]);
  // other code
}

ce qui laisse le calcul de parents prendre au moins 5 secondes.

425
Bergi

Depuis Node 7.6, vous pouvez combiner les fonctions promisify à partir du module utils avec setTimeout().

Node.js

const sleep = require('util').promisify(setTimeout)

Javascript

const sleep = m => new Promise(r => setTimeout(r, m))

Usage

(async () => {
    console.time("Slept for")
    await sleep(3000)
    console.timeEnd("Slept for")
})()
95
Harry

Le one-liner rapide, en ligne

 await new Promise(resolve => setTimeout(resolve, 1000));
48
FlavorScape

setTimeout n'est pas une fonction async, vous ne pouvez donc pas l'utiliser avec ES7 async-wait. Mais vous pouvez implémenter votre fonction sleep en utilisant ES6 Promise :

function sleep (fn, par) {
  return new Promise((resolve) => {
    // wait 3s before calling fn(par)
    setTimeout(() => resolve(fn(par)), 3000)
  })
}

Vous pourrez ensuite utiliser cette nouvelle fonction sleep avec ES7 async-wait:

var fileList = await sleep(listFiles, nextPageToken)

Veuillez noter que je ne réponds qu'à votre question sur la combinaison de l'ES7 asynchrone/wait avec setTimeout, bien que cela ne vous aide peut-être pas à résoudre votre problème. avec l'envoi de trop de demandes par seconde.


Mise à jour: Les versions modernes de node.js disposent d'une implémentation asynchrone buid-in, accessible via til.promisify helper:

const {promisify} = require('util');
const setTimeoutAsync = promisify(setTimeout);
26
Leonid Beschastny

Si vous souhaitez utiliser le même type de syntaxe que setTimeout, vous pouvez écrire une fonction d'assistance comme celle-ci:

const setAsyncTimeout = (cb, timeout = 0) => new Promise(resolve => {
    setTimeout(() => {
        cb();
        resolve();
    }, timeout);
});

Vous pouvez alors l'appeler comme suit:

const doStuffAsync = async () => {
    await setAsyncTimeout(() => {
        // Do stuff
    }, 1000);

    await setAsyncTimeout(() => {
        // Do more stuff
    }, 500);

    await setAsyncTimeout(() => {
        // Do even more stuff
    }, 2000);
};

doStuffAsync();

J'ai fait un Gist: https://Gist.github.com/DaveBitter/f44889a2a52ad16b6a5129c39444bb57

1
Dave Bitter

Ceci est une solution plus rapide dans une ligne.

J'espère que cela aidera.

// WAIT FOR 200 MILISECONDS TO GET DATA //
await setTimeout(()=>{}, 200);
0
Rommy Garg

Le code suivant fonctionne dans Chrome, Firefox et peut-être d'autres navigateurs.

function timeout(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}
async function sleep(fn, ...args) {
    await timeout(3000);
    return fn(...args);
}

Mais dans Internet Explorer, une erreur de syntaxe est générée pour le "(resolve **=>** setTimeout..."

0
Shadowned