Rufen Sie async/await-Funktionen parallel auf

Lesezeit: 13 Minuten

Rufen Sie asyncawait Funktionen parallel auf
Victor Marchuk

Soweit ich weiß, in ES7/ES2016 mehrere setzen await‘s im Code funktionieren ähnlich wie das Verketten .then() mit Versprechen, was bedeutet, dass sie nacheinander und nicht parallel ausgeführt werden. Also haben wir zum Beispiel diesen Code:

await someCall();
await anotherCall();

Verstehe ich das richtig anotherCall() wird nur wann angerufen someCall() abgeschlossen? Wie kann man sie am elegantesten parallel aufrufen?

Ich möchte es in Node verwenden, also gibt es vielleicht eine Lösung mit einer asynchronen Bibliothek?

BEARBEITEN: Ich bin mit der in dieser Frage bereitgestellten Lösung nicht zufrieden: Verlangsamung durch nicht paralleles Warten auf Versprechen in asynchronen Generatoren, da Generatoren verwendet werden und ich nach einem allgemeineren Anwendungsfall frage.

  • @ Blindman67 – das tut es, zumindest so, wie das OP meint, dass zwei asynchrone Operationen gleichzeitig ausgeführt werden, aber nicht in diesem Fall wollte ich schreiben, dass sie einlaufen seriellDer Erste await würde warten, bis die erste Funktion vollständig abgeschlossen ist, bevor die zweite ausgeführt wird.

    – adeneo

    24. Februar 2016 um 20:46 Uhr


  • @ Blindman67 – es ist ein Single-Thread, aber diese Einschränkung gilt nicht für asynchrone Methoden, sie kann laufen gleichzeitig und geben die Antwort zurück, wenn sie fertig sind, dh was das OP mit “parallel” meint.

    – adeneo

    24. Februar 2016 um 20:56 Uhr


  • @ Blindman67 – Ich denke, es ist ziemlich klar, was das OP fragt. Durch die Verwendung des async / await-Musters werden die Funktionen seriell ausgeführt, auch wenn sie asynchron sind, sodass die erste vollständig beendet wird, bevor die zweite aufgerufen wird usw. Das OP ist fragen, wie man beide Funktionen parallel aufruft, und da sie eindeutig asynchron sind, ist das Ziel, sie gleichzeitig auszuführen, dh parallel, zum Beispiel zwei Ajax-Anfragen gleichzeitig zu machen, was in Javascript überhaupt kein Problem ist, wie die meisten asynchronen Methoden , wie Sie bemerkt haben, führt nativen Code aus und verwendet mehr Threads.

    – adeneo

    24. Februar 2016 um 21:27 Uhr


  • @Bergi dies ist kein Duplikat der verknüpften Frage – hier geht es speziell um async/await-Syntax und native PromiseS. Die verknüpfte Frage bezieht sich auf die Bluebird-Bibliothek mit Generatoren und Ertrag. Konzeptionell vielleicht ähnlich, aber nicht in der Umsetzung.

    – Äh

    10. Mai 2017 um 15:19 Uhr

  • @Bergi Die Syntax ist sehr wichtig. Für eine Person, die noch nie Generatoren oder Bluebird verwendet hat, ist die verknüpfte Frage völlig nutzlos.

    – Äh

    11. Mai 2017 um 17:42 Uhr

Rufen Sie asyncawait Funktionen parallel auf
madox2

Sie können weiter warten Promise.all():

await Promise.all([someCall(), anotherCall()]);

So speichern Sie die Ergebnisse:

let [someResult, anotherResult] = await Promise.all([someCall(), anotherCall()]);

Beachten Sie, dass Promise.all schnell fehlschlägt, was bedeutet, dass sobald eines der ihm zugeführten Versprechen ablehnt, das Ganze ablehnt.

const happy = (v, ms) => new Promise((resolve) => setTimeout(() => resolve(v), ms))
const sad = (v, ms) => new Promise((_, reject) => setTimeout(() => reject(v), ms))

Promise.all([happy('happy', 100), sad('sad', 50)])
  .then(console.log).catch(console.log) // 'sad'

Wenn Sie stattdessen warten möchten, bis alle Versprechen entweder erfüllt oder abgelehnt werden, können Sie verwenden Promise.allSettled. Beachten Sie, dass Internet Explorer diese Methode nicht nativ unterstützt.

const happy = (v, ms) => new Promise((resolve) => setTimeout(() => resolve(v), ms))
const sad = (v, ms) => new Promise((_, reject) => setTimeout(() => reject(v), ms))

Promise.allSettled([happy('happy', 100), sad('sad', 50)])
  .then(console.log) // [{ "status":"fulfilled", "value":"happy" }, { "status":"rejected", "reason":"sad" }]

Notiz: Wenn du benutzt Promise.all Aktionen, die abgeschlossen werden konnten, bevor die Ablehnung erfolgte, werden nicht zurückgesetzt, daher müssen Sie sich möglicherweise um eine solche Situation kümmern. Wenn Sie beispielsweise 5 Aktionen haben, 4 schnelle, 1 langsame und langsame Ablehnungen. Diese 4 Aktionen wurden möglicherweise bereits ausgeführt, sodass Sie möglicherweise einen Rollback durchführen müssen. Ziehen Sie in einer solchen Situation die Verwendung in Betracht Promise.allSettled während es genaue Details liefert, welche Aktion fehlgeschlagen ist und welche nicht.

  • Sauber, aber beachten Sie das Fast-Fail-Verhalten von Promise.all. Wenn eine der Funktionen einen Fehler auslöst, lehnt Promise.all ab

    – NoNamebereitgestellt

    8. Februar 2017 um 12:26 Uhr

  • Sie können Teilergebnisse gut mit async/await handhaben, siehe stackoverflow.com/a/42158854/2019689

    – NoNamebereitgestellt

    27. März 2017 um 12:42 Uhr

  • Profi-Tipp: Verwenden Sie die Array-Destrukturierung, um eine beliebige Anzahl von Ergebnissen von Promise.all() zu initialisieren, wie zum Beispiel: [result1, result2] = Promise.all([async1(), async2()]);

    – jonny

    10. Februar 2018 um 13:19 Uhr

  • @jonny Ist dies Gegenstand von Fail-Fast? Muss man auch noch = await Promise.all?

    – theUtherSide

    12. Juli 2018 um 4:51 Uhr


  • @theUtherSide Sie haben absolut Recht – ich habe es versäumt, das Erwarten einzubeziehen.

    – jonny

    12. Juli 2018 um 9:57 Uhr

Rufen Sie asyncawait Funktionen parallel auf
Oase

TL;DR

Verwenden Promise.all Bei den parallelen Funktionsaufrufen verhält sich das Antwortverhalten nicht korrekt, wenn der Fehler auftritt.


Zuerst ausführen alle die asynchronen Aufrufe auf einmal und erhalten alle Promise Objekte. Zweitens verwenden await auf der Promise Objekte. Auf diese Weise, während Sie auf den ersten warten Promise zur Auflösung der anderen asynchronen Aufrufe sind noch im Gange. Insgesamt warten Sie nur so lange wie der langsamste asynchrone Aufruf. Zum Beispiel:

// Begin first call and store promise without waiting
const someResult = someCall();

// Begin second call and store promise without waiting
const anotherResult = anotherCall();

// Now we await for both results, whose async processes have already been started
const finalResult = [await someResult, await anotherResult];

// At this point all calls have been resolved
// Now when accessing someResult| anotherResult,
// you will have a value instead of a promise

JSbin-Beispiel: http://jsbin.com/xerifanima/edit?js,console

Vorbehalt: Es spielt keine Rolle, ob die await Anrufe sind auf der gleichen Leitung oder auf verschiedenen Leitungen, solange die erste await Anruf passiert nach dem alle asynchronen Aufrufe. Siehe JohnnyHKs Kommentar.


Aktualisieren: Diese Antwort hat ein anderes Timing bei der Fehlerbehandlung gemäß der Antwort von @bergi NICHT Werfen Sie den Fehler aus, wenn der Fehler auftritt, aber nachdem alle Promises ausgeführt wurden. Ich vergleiche das Ergebnis mit dem Tipp von @jonny: [result1, result2] = Promise.all([async1(), async2()])überprüfen Sie das folgende Code-Snippet

const correctAsync500ms = () => {
  return new Promise(resolve => {
    setTimeout(resolve, 500, 'correct500msResult');
  });
};

const correctAsync100ms = () => {
  return new Promise(resolve => {
    setTimeout(resolve, 100, 'correct100msResult');
  });
};

const rejectAsync100ms = () => {
  return new Promise((resolve, reject) => {
    setTimeout(reject, 100, 'reject100msError');
  });
};

const asyncInArray = async (fun1, fun2) => {
  const label="test async functions in array";
  try {
    console.time(label);
    const p1 = fun1();
    const p2 = fun2();
    const result = [await p1, await p2];
    console.timeEnd(label);
  } catch (e) {
    console.error('error is', e);
    console.timeEnd(label);
  }
};

const asyncInPromiseAll = async (fun1, fun2) => {
  const label="test async functions with Promise.all";
  try {
    console.time(label);
    let [value1, value2] = await Promise.all([fun1(), fun2()]);
    console.timeEnd(label);
  } catch (e) {
    console.error('error is', e);
    console.timeEnd(label);
  }
};

(async () => {
  console.group('async functions without error');
  console.log('async functions without error: start')
  await asyncInArray(correctAsync500ms, correctAsync100ms);
  await asyncInPromiseAll(correctAsync500ms, correctAsync100ms);
  console.groupEnd();

  console.group('async functions with error');
  console.log('async functions with error: start')
  await asyncInArray(correctAsync500ms, rejectAsync100ms);
  await asyncInPromiseAll(correctAsync500ms, rejectAsync100ms);
  console.groupEnd();
})();

  • Das sieht für mich nach einer viel schöneren Option aus als Promise.all – und mit destrukturierender Zuweisung können Sie das sogar tun [someResult, anotherResult] = [await someResult, await anotherResult] wenn du dich änderst const zu let.

    – Jawj

    25. August 2017 um 13:26 Uhr


  • Aber das führt immer noch die await Aussagen seriell, oder? Das heißt, die Ausführung pausiert bis zum ersten await löst sich auf und geht dann zur zweiten über. Promise.all wird parallel ausgeführt.

    – Andru

    26. August 2017 um 11:56 Uhr


  • Diese Antwort ist irreführend, da die Tatsache, dass beide Wartevorgänge in derselben Zeile ausgeführt werden, irrelevant ist. Wichtig ist, dass die beiden asynchronen Aufrufe getätigt werden, bevor auf einen der beiden gewartet wird.

    – JohnnyHK

    16. Oktober 2017 um 3:09 Uhr

  • @Haven Diese Lösung ist nicht dasselbe wie Promise.all. Wenn jede Anfrage ein Netzwerkanruf ist, await someResult muss vorher gelöst werden await anotherResult wird sogar gestartet. Umgekehrt hinein Promise.all die Zwei await Anrufe können gestartet werden, bevor einer der beiden aufgelöst wird.

    – Ben Wicklung

    7. Februar 2018 um 4:59 Uhr

  • Diese Antwort ist einfach falsch und sollte gelöscht werden.

    – Golo Roden

    4. September 2018 um 17:53 Uhr

1646311520 10 Rufen Sie asyncawait Funktionen parallel auf
Jonathan Potter

Aktualisieren:

Die ursprüngliche Antwort macht es schwierig (und in einigen Fällen unmöglich), mit Ablehnungen von Versprechen richtig umzugehen. Die richtige Lösung ist zu verwenden Promise.all:

const [someResult, anotherResult] = await Promise.all([someCall(), anotherCall()]);

Ursprüngliche Antwort:

Stellen Sie nur sicher, dass Sie beide Funktionen aufrufen, bevor Sie auf eine warten:

// Call both functions
const somePromise = someCall();
const anotherPromise = anotherCall();

// Await both promises    
const someResult = await somePromise;
const anotherResult = await anotherPromise;

  • Ich würde dem Code einen Kommentar hinzufügen, weil nichts besagt, dass der nächste Entwickler verstehen wird, was Sie tun, OOB.

    – Jeff Fischer

    7. Januar 2018 um 17:44 Uhr

  • Ich habe das Gefühl, dass dies sicherlich die reinste Antwort ist

    – Gershom Maes

    18. April 2018 um 15:44 Uhr

  • Diese Antwort ist viel klarer als die von Haven. Es ist klar, dass die Funktionsaufrufe Promise-Objekte und zurückgeben await löst sie dann in tatsächliche Werte auf.

    – Benutzer1032613

    5. September 2018 um 0:46 Uhr

  • Dies scheint auf den ersten Blick zu funktionieren, hat aber schreckliche Probleme mit unbehandelten Ablehnungen. Verwenden Sie dies nicht!

    – Bergi

    20. Januar 2019 um 19:51 Uhr


  • @Bergi Du hast Recht, danke für den Hinweis! Ich habe die Antwort mit einer besseren Lösung aktualisiert.

    – Jonathan Potter

    12. März 2020 um 14:25 Uhr

Es gibt eine andere Möglichkeit ohne Promise.all(), dies parallel zu tun:

Zuerst haben wir 2 Funktionen zum Drucken von Zahlen:

function printNumber1() {
   return new Promise((resolve,reject) => {
      setTimeout(() => {
      console.log("Number1 is done");
      resolve(10);
      },1000);
   });
}

function printNumber2() {
   return new Promise((resolve,reject) => {
      setTimeout(() => {
      console.log("Number2 is done");
      resolve(20);
      },500);
   });
}

Das ist der Reihe nach:

async function oneByOne() {
   const number1 = await printNumber1();
   const number2 = await printNumber2();
} 
//Output: Number1 is done, Number2 is done

Das ist parallel:

async function inParallel() {
   const promise1 = printNumber1();
   const promise2 = printNumber2();
   const number1 = await promise1;
   const number2 = await promise2;
}
//Output: Number2 is done, Number1 is done

1646311521 295 Rufen Sie asyncawait Funktionen parallel auf
SkarXa

Ich habe erstellt ein wesentliches Testen einiger verschiedener Möglichkeiten, Versprechen aufzulösen, mit Ergebnissen. Es kann hilfreich sein, die Optionen zu sehen, die funktionieren.

Bearbeiten: Kerninhalt gemäß Jin Lees Kommentar

// Simple gist to test parallel promise resolution when using async / await

function promiseWait(time) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(true);
    }, time);
});
}


async function test() {
    return [
    await promiseWait(1000),
    await promiseWait(5000),
    await promiseWait(9000),
    await promiseWait(3000),
    ]
}

async function test2() {
    return {
        'aa': await promiseWait(1000),
        'bb': await promiseWait(5000),
        'cc': await promiseWait(9000),
        'dd': await promiseWait(3000),
    }
}

async function test3() {
    return await {
        'aa': promiseWait(1000),
        'bb': promiseWait(5000),
        'cc': promiseWait(9000),
        'dd': promiseWait(3000),
    }
}

async function test4() {
    const p1 =  promiseWait(1000);
    const p2 =  promiseWait(5000);
    const p3 =  promiseWait(9000);
    const p4 =  promiseWait(3000);
    return {
        'aa': await p1,
        'bb': await p2,
        'cc': await p3,
        'dd': await p4,
    };
}

async function test5() {
    return await Promise.all([
                             await promiseWait(1000),
                             await promiseWait(5000),
                             await promiseWait(9000),
                             await promiseWait(3000),
                             ]);
}

async function test6() {
    return await Promise.all([
                             promiseWait(1000),
                             promiseWait(5000),
                             promiseWait(9000),
                             promiseWait(3000),
                             ]);
}

async function test7() {
    const p1 =  promiseWait(1000);
    const p2 =  promiseWait(5000);
    const p3 =  promiseWait(9000);
    return {
        'aa': await p1,
        'bb': await p2,
        'cc': await p3,
        'dd': await promiseWait(3000),
    };
}

let start = Date.now();

test().then((res) => {
    console.log('Test Done, elapsed', (Date.now() - start) / 1000, res);

    start = Date.now();
    test2().then((res) => {
        console.log('Test2 Done, elapsed', (Date.now() - start) / 1000, res);

        start = Date.now();
        test3().then((res) => {
            console.log('Test3 Done, elapsed', (Date.now() - start) / 1000, res);

            start = Date.now();
            test4().then((res) => {
                console.log('Test4 Done, elapsed', (Date.now() - start) / 1000, res);

                start = Date.now();
                test5().then((res) => {
                    console.log('Test5 Done, elapsed', (Date.now() - start) / 1000, res);

                    start = Date.now();
                    test6().then((res) => {
                        console.log('Test6 Done, elapsed', (Date.now() - start) / 1000, res);
                    });

                    start = Date.now();
                    test7().then((res) => {
                        console.log('Test7 Done, elapsed', (Date.now() - start) / 1000, res);
                    });
                });
            });

        });
    });

});
/*
Test Done, elapsed 18.006 [ true, true, true, true ]
Test2 Done, elapsed 18.009 { aa: true, bb: true, cc: true, dd: true }
Test3 Done, elapsed 0 { aa: Promise { <pending> },
  bb: Promise { <pending> },
  cc: Promise { <pending> },
  dd: Promise { <pending> } }
Test4 Done, elapsed 9 { aa: true, bb: true, cc: true, dd: true }
Test5 Done, elapsed 18.008 [ true, true, true, true ]
Test6 Done, elapsed 9.003 [ true, true, true, true ]
Test7 Done, elapsed 12.007 { aa: true, bb: true, cc: true, dd: true }
*/

  • Die Tests 4 und 6 lieferten im Wesentlichen die erwarteten Ergebnisse. Siehe stackoverflow.com/a/42158854/5683904 von NoNameProvided, der den Unterschied zwischen den Optionen erklärt.

    – Akraine

    4. Oktober 2018 um 15:43 Uhr


  • Während dieser Link die Frage beantworten kann, ist es besser, die wesentlichen Teile der Antwort hier einzufügen und den Link als Referenz bereitzustellen. Nur-Link-Antworten können ungültig werden, wenn sich die verlinkte Seite ändert. – Aus Bewertung

    – Jin Lee

    3. März 2021 um 12:42 Uhr

  • @JinLee Gemäß Ihrem Vorschlag habe ich den Inhalt hinzugefügt, ihn ursprünglich nicht hinzugefügt, weil er nach viel Inhalt aussah

    – SkarXa

    3. März 2021 um 14:53 Uhr

  • @SkarXa SO wird deine Antwort jetzt noch mehr gefallen. 🙂 Und dein Code ist nicht so lang. Mach dir keine Sorge. Danke!

    – Jin Lee

    3. März 2021 um 17:47 Uhr

1646311522 277 Rufen Sie asyncawait Funktionen parallel auf
Alex Dresko

In meinem Fall habe ich mehrere Aufgaben, die ich parallel ausführen möchte, aber ich muss etwas anderes mit dem Ergebnis dieser Aufgaben machen.

function wait(ms, data) {
    console.log('Starting task:', data, ms);
    return new Promise(resolve => setTimeout(resolve, ms, data));
}

var tasks = [
    async () => {
        var result = await wait(1000, 'moose');
        // do something with result
        console.log(result);
    },
    async () => {
        var result = await wait(500, 'taco');
        // do something with result
        console.log(result);
    },
    async () => {
        var result = await wait(5000, 'burp');
        // do something with result
        console.log(result);
    }
]

await Promise.all(tasks.map(p => p()));
console.log('done');

Und die Ausgabe:

Starting task: moose 1000
Starting task: taco 500
Starting task: burp 5000
taco
moose
burp
done

  • Die Tests 4 und 6 lieferten im Wesentlichen die erwarteten Ergebnisse. Siehe stackoverflow.com/a/42158854/5683904 von NoNameProvided, der den Unterschied zwischen den Optionen erklärt.

    – Akraine

    4. Oktober 2018 um 15:43 Uhr


  • Während dieser Link die Frage beantworten kann, ist es besser, die wesentlichen Teile der Antwort hier einzufügen und den Link als Referenz bereitzustellen. Nur-Link-Antworten können ungültig werden, wenn sich die verlinkte Seite ändert. – Aus Bewertung

    – Jin Lee

    3. März 2021 um 12:42 Uhr

  • @JinLee Gemäß Ihrem Vorschlag habe ich den Inhalt hinzugefügt, ihn ursprünglich nicht hinzugefügt, weil er nach viel Inhalt aussah

    – SkarXa

    3. März 2021 um 14:53 Uhr

  • @SkarXa SO wird deine Antwort jetzt noch mehr gefallen. 🙂 Und dein Code ist nicht so lang. Mach dir keine Sorge. Danke!

    – Jin Lee

    3. März 2021 um 17:47 Uhr

1643712313 933 Beschranken Sie die Gleichzeitigkeit der ausgefuhrten Zusage
Thiago Conrado

warte auf Promise.all([someCall(), anotherCall()]); Wie bereits erwähnt, fungiert es als Thread-Zaun (sehr häufig in parallelem Code wie CUDA), daher ermöglicht es, dass alle darin enthaltenen Versprechen ausgeführt werden, ohne sich gegenseitig zu blockieren, verhindert jedoch, dass die Ausführung fortgesetzt wird, bis ALLE aufgelöst sind.

Ein weiterer Ansatz, der es wert ist, geteilt zu werden, ist Node.js Async, mit dem Sie auch die Menge an Parallelität, die normalerweise wünschenswert ist, einfach steuern können, wenn die Aufgabe direkt mit der Verwendung begrenzter Ressourcen wie API-Aufruf, E/A-Operationen, usw.

// create a queue object with concurrency 2
var q = async.queue(function(task, callback) {
  console.log('Hello ' + task.name);
  callback();
}, 2);

// assign a callback
q.drain = function() {
  console.log('All items have been processed');
};

// add some items to the queue
q.push({name: 'foo'}, function(err) {
  console.log('Finished processing foo');
});

q.push({name: 'bar'}, function (err) {
  console.log('Finished processing bar');
});

// add some items to the queue (batch-wise)
q.push([{name: 'baz'},{name: 'bay'},{name: 'bax'}], function(err) {
  console.log('Finished processing item');
});

// add some items to the front of the queue
q.unshift({name: 'bar'}, function (err) {
  console.log('Finished processing bar');
});

Credits an den Autor des Medium-Artikels (Weiterlesen)

  • Drosseln .map, .filter und .mapSeries helfen auch bei der Parallelität, wenn Sie die Callback-API für asynchrone Module nicht einführen möchten

    – Matt

    24. Juli 2020 um 1:51 Uhr


923510cookie-checkRufen Sie async/await-Funktionen parallel auf

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy