Je ne peux pas partager mon code exact - mais j'ai essayé d'illustrer ma situation ci-dessous:
L'API distante a une requête max. limite de 10 demandes / pà © r sec. et je voudrais accélérer mon code pour me rapprocher de cette limite. Actuellement, le code exécute 1 à 2 requêtes par seconde.
Exemple - Récupérez 10 heures de données pour 100 personnes différentes:
(async function get(...) { await getPersonData(for one person); if (not all persons' data has been fetched) { get(fetch data for the next person); } })(...); async function getPersonData() { const personData = await getHistoricalData(...); ... }; async function getHistoricalData(...) { // Fetch 10 hours of data ... while (as long as all data has not yet been fetch...) { const data = await getOneHourOfData(...); ... } return all_20_hours_of_data; } async function getOneHourOfData(...) { return await remote.api.getData(get 1 hour of data); }
L'exemple ci-dessus est ma version standard de mon code - j'ai également essayé deux approches différentes:
les deux méthodes ont fonctionné - mais aucune d'entre elles ne semble accélérer quoi que ce soit ... ?? J'ai une idée que c'est la boucle while qui bloque / ralentit tout le processus?
3 Réponses :
Je préfère utiliser Promise.all
.
await new Promise((resolve, reject) => setTimeout(resolve, 1000))
Maintenant, je suis très curieux de savoir while (tant que toutes les données n'ont pas encore été récupérées ...) {
expression.
Est-ce peut-être
const data = await Promise.all([ getOneHourOfData(params) ... // the same as above but different params x times ])
?
Non - c'était juste une autre façon de dire: tirez 1 heure de données à chaque itération jusqu'à ce que toutes les données aient été récupérées (10 itérations / 10 heures dans l'exemple).
Le code de votre question ressemble effectivement à ceci:
(async function get() { try { console.time("get"); console.log(JSON.stringify(await getPersonData())); console.timeEnd("get"); } catch (e) { console.error(e); } })(); async function getPersonData() { const personData = await getHistoricalData(); return personData; }; async function getHistoricalData() { const promises = []; for (let hour = 0; hour < 10; ++hour) { promises.push(getOneHourOfData()); // <== No `await`! } return Promise.all(promises); // <== `await `on this line is optional but // pointless, this is an `async` // function, so its promise will be // resolved to the promise we return } function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } let num = 0; async function getOneHourOfData() { await delay(150); return ++num; }
L'exécution prend environ 1500 ms.
Voici la même chose en effectuant les appels de 10 "heures" en parallèle et en utilisant Promise.all
: p >
(async function get() { try { console.time("get"); console.log(JSON.stringify(await getPersonData())); console.timeEnd("get"); } catch (e) { console.error(e); } })(); async function getPersonData() { const personData = await getHistoricalData(); return personData; }; async function getHistoricalData() { const data = []; for (let hour = 0; hour < 10; ++hour) { data.push(await getOneHourOfData()); } return data; } function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } let num = 0; async function getOneHourOfData() { await delay(150); return ++num; }
Cela dure environ 150 ms, car les 10 appels de données historiques se produisent en parallèle. Notez que l'essentiel est de constituer un tableau de promesses (sans les attendre
), puis d'utiliser Promise.all
pour obtenir une seule promesse pour tout ce tableau de promesses .
aahh! Quand j'ai essayé d'utiliser Promise.all (), je ne l'ai pas fait sur la fonction getOneHourOfData - mais sur la fonction get () ... je suppose - cela doit être la raison pour laquelle cela fonctionne - mais sans aucune amélioration de la vitesse! Merci beaucoup mon ami! Je vais l'essayer et vous revenir dès que possible! :) Merci encore! Btw - utilisez-vous personnellement Promise.all (toutes les tâches de votre vie en une journée) ... sinon, comment obtenez-vous le temps d'obtenir 147 badges d'or !? MDR! Impressionant!
@PabloDK - LOL! Cela fonctionnerait si vous le faisiez également dans get
, mais l'essentiel est de constituer le tableau des promesses sans les attendre, puis attendez
le résultat de Promise.all
. Bon codage!
J'ai réalisé plus tôt dans la journée que le client API que j'utilise avait une limite de débit max intégrée qui ajoutait une pause de 500 ms entre chaque appel d'API! Donc pas étonnant que je ne puisse pas faire fonctionner mon code rapidement! MDR! L'API à distance autorise 20 appels par / secondes - quelle est la meilleure façon / meilleure pratique pour contrôler cela? Actuellement, j'ai mis la limite de débit intégrée à une pause de 50 ms après chaque appel (1000 ms / 50 ms = 20 appel / s) ... Je démarre et exécute ensuite 50 promesses et promesse simultanément. charger les 50 prochains ... cela semble fonctionner ... Mais je suppose que cela peut encore être amélioré?
@PabloDK - Cela me semble raisonnable. :-)
Bon alors :-) et vous n'ajouteriez pas une sorte de couche supplémentaire pour contrôler ce flux? Peut-être via un module NPM rapide ou similaire? :-)
@PabloDK - Je ne connais pas de package npm spécifique qui le ferait, mais oui, je pourrais faire un wrapper de limitation de débit pour axios
ou quelque chose du genre. Mais s'il ne s'agit que de cette API, cela pourrait être excessif à moins que vous ne l'utilisiez dans de nombreux endroits.
Je viens de terminer la lecture de l'intégralité du DOCS de ce module NPM (il semble assez solide et exactement ce que j'avais en tête): npmjs.com/package/bottleneck . Je ne me sens pas à 100% en sécurité avec la limite de débit intégrée dans mon client API actuel (je peux toujours dépasser la limite de débit réelle (et recevoir l'erreur 429 de l'API distante) - cela ne semble pas prendre de surcharge / beaucoup d'appels simultanés en considération). Je préfère utiliser un module dédié pour cela à la place - ce concept comprend la mise en file d'attente, l'exécution atomique et max appelle simultanément :-D
Vous pouvez utiliser un sémaphore (très spécial) pour limiter les appels d'API à un certain taux:
const apiLimit = new TimeSemaphore(1000, 5); async function callAPI() { await apiLimit.aquire(async function() { await fetch(...); }); } callAPI(); // as often as you want
Ceci peut être utilisé comme:
class TimeSemaphore { #times = []; #backlog = Promise.resolve(); constructor(interval, parallel) { this.interval = interval; this.parallel = parallel; } async aquire(cb) { this.#backlog = this.#backlog.then(() => { if(this.#times.length >= this.parallel && Date.now() - this.#times[0] < this.interval) return new Promise(res => setTimeout(res, this.interval - (Date.now() - this.#times[0])); }); this.#times.push(Date.now()); await this.#backlog; try { return await cb(); } finally { this.#times.shift(); } } }
Cela a l'air sophistiqué - pourriez-vous s'il vous plaît expliquer ce que fait le code et comment ... ce n'est pas clair pour moi :-)
Les deux choses que vous avez dites que vous avez essayées devraient fonctionner, suggérant qu'il y avait un problème pour mettre en œuvre ces idées. Nous ne pouvons pas vous aider avec du code que nous ne pouvons pas voir. :-) Veuillez nous montrer le code que vous avez essayé d'utiliser pour ceux-ci. Veuillez publier du code réel , et non un pseudocode, idéalement exécutable via Stack Snippets . Il n'est pas nécessaire que ce soit votre vrai code bien sûr, et vous voudrez émuler le
remote.api.getData
avec unsetTimeout
, mais en faire un vrai code supprime ambiguïté et nous aide à vous aider.Je comprends parfaitement. Mais en raison du fait que j'ai réussi à faire fonctionner tous les exemples de code - mais ils n'ont pas accéléré tout le processus ... alors j'ai pensé qu'un psydo-code / concept le ferait. Mais d'accord - je vais essayer de trouver quelque chose de plus concret si je ne fais aucune erreur évidente dans l'exemple ci-dessus :)
Nous pouvons toujours offrir de meilleures réponses si nous voyons le vrai code et le principal avantage pour vous est que nous pouvons même offrir des suggestions / améliorations que vous n'avez même pas pensé demander. Pour une raison inconnue, les gens qui publient ici pensent qu'ils devraient poser une question générique avec un pseudo-code - ce n'est presque jamais la meilleure option pour vous. Veuillez toujours montrer le code réel pertinent et nous pouvons vous aider plus précisément et avec du code réel que vous pouvez utiliser et pouvons vous aider d'une manière que vous ne saviez même pas demander.