Asynchrones JavaScript: Rückrufe und Versprechen erklärt

Der Umgang mit asynchronem Code, dh Code, der nicht sofort wie Webanforderungen oder Timer ausgeführt wird, kann schwierig sein. JavaScript bietet uns zwei Möglichkeiten, um mit asynchronem Verhalten umzugehen: Rückrufe und Versprechen.

Rückrufe waren die einzige nativ unterstützte Methode für den Umgang mit asynchronem Code, bis 2016 das PromiseObjekt in die Sprache eingeführt wurde. JavaScript-Entwickler hatten jedoch bereits Jahre zuvor ähnliche Funktionen implementiert, bevor Versprechungen auf den Plan kamen. Schauen wir uns einige der Unterschiede zwischen Rückrufen und Versprechungen an und sehen wir, wie wir mit der Koordination mehrerer Versprechungen umgehen.

Asynchrone Funktionen, die Rückrufe verwenden, verwenden eine Funktion als Parameter, die nach Abschluss der Arbeit aufgerufen wird. Wenn Sie so etwas wie setTimeoutim Browser verwendet haben, haben Sie Rückrufe verwendet.

// Sie können Ihren Rückruf separat definieren ...

lass myCallback = () => {

  console.log ('Called!');

};

setTimeout (myCallback, 3000);

//… aber es ist auch üblich, dass Rückrufe inline definiert werden

setTimeout (() => {

  console.log ('Called!');

}, 3000);

Normalerweise nimmt die Funktion, die einen Rückruf entgegennimmt, ihn als letztes Argument. Dies ist oben nicht der Fall. Nehmen wir also an, es gibt eine neue Funktion namens wait, die genau so ist, setTimeoutaber die ersten beiden Argumente in umgekehrter Reihenfolge verwendet:

// Wir würden unsere neue Funktion folgendermaßen verwenden:

waitCallback (3000, () => {

  console.log ('Called!');

});

Verschachtelte Rückrufe und die Pyramide des Untergangs

Rückrufe funktionieren gut für die Verarbeitung von asynchronem Code, werden jedoch schwierig, wenn Sie mehrere asynchrone Funktionen koordinieren müssen. Wenn wir beispielsweise zwei Sekunden warten und etwas protokollieren möchten, dann drei Sekunden warten und etwas anderes protokollieren möchten, dann vier Sekunden warten und etwas anderes protokollieren möchten, wird unsere Syntax tief verschachtelt.

// Wir würden unsere neue Funktion folgendermaßen verwenden:

waitCallback (2000, () => {

  console.log ('Erster Rückruf!');

  waitCallback (3000, () => {

    console.log ('Zweiter Rückruf!');

    waitCallback (4000, () => {

      console.log ('Dritter Rückruf!');

    });

  });

});

Dies mag wie ein triviales Beispiel erscheinen (und ist es auch), aber es ist nicht ungewöhnlich, mehrere Webanfragen hintereinander zu stellen, basierend auf den Rückgabeergebnissen einer vorherigen Anfrage. Wenn Ihre AJAX-Bibliothek Rückrufe verwendet, wird die obige Struktur angezeigt. In Beispielen, die tiefer verschachtelt sind, sehen Sie die sogenannte Untergangspyramide, die ihren Namen von der Pyramidenform hat, die im eingerückten Leerzeichen am Anfang der Zeilen erstellt wurde.

Wie Sie sehen können, wird unser Code strukturell entstellt und ist schwerer zu lesen, wenn asynchrone Funktionen verarbeitet werden, die nacheinander ausgeführt werden müssen. Aber es wird noch schwieriger. Stellen Sie sich vor, wir möchten drei oder vier Webanfragen initiieren und eine Aufgabe erst ausführen, nachdem alle zurückgekehrt sind. Ich ermutige Sie, es zu versuchen, wenn Sie die Herausforderung noch nicht erlebt haben.

Einfacher asynchron mit Versprechungen

Versprechen bieten eine flexiblere API für die Bearbeitung asynchroner Aufgaben. Die Funktion muss so geschrieben sein, dass sie ein PromiseObjekt zurückgibt , das einige Standardfunktionen für die Behandlung nachfolgenden Verhaltens und die Koordination mehrerer Versprechen enthält. Wenn unsere waitCallbackFunktion Promise-basiert wäre, würde nur ein Argument benötigt, nämlich die Millisekunden, um zu warten. Jede spätere Funktionalität würde gekettet aus dem Versprechen. Unser erstes Beispiel würde so aussehen:

lass myHandler = () => {

  console.log ('Called!');

};

waitPromise (3000) .then (myHandler);

Gibt im obigen Beispiel waitPromise(3000)ein PromiseObjekt zurück, das einige Methoden enthält, die wir verwenden können, z then. Wenn wir mehrere asynchrone Funktionen nacheinander ausführen wollten, könnten wir die Pyramide des Untergangs vermeiden, indem wir Versprechen verwenden. Dieser Code, der neu geschrieben wurde, um unser neues Versprechen zu unterstützen, würde folgendermaßen aussehen:

// Egal wie viele sequentielle asynchrone Aufgaben wir haben, wir machen niemals die Pyramide.

waitPromise (2000)

  .then (() => {

    console.log ('Erster Rückruf!');

    return waitPromise (3000);

  })

  .then (() => {

    console.log ('Zweiter Rückruf!');

    return waitPromise (4000);

  })

  .then (() => {

    console.log ('Zweiter Rückruf!');

    return waitPromise (4000);

  });

Besser noch, wenn wir asynchrone Aufgaben koordinieren müssen, die Versprechen unterstützen, könnten wir alleine statische Methode für das PromiseObjekt verwenden, die mehrere Versprechen nimmt und sie zu einem kombiniert. Das würde so aussehen:

Promise.all ([

  waitPromise (2000),

  waitPromise (3000),

  waitPromise (4000)

]). then (() => console.log ('Alles ist erledigt!'));

Nächste Woche werden wir uns eingehender damit befassen, wie Versprechen funktionieren und wie sie idiomatisch verwendet werden. Wenn Sie nur JavaScript lernen oder Ihr Wissen testen möchten, versuchen Sie, waitCallbackdas Äquivalent von Promise.allRückrufen zu erreichen.

Wie immer, kontaktieren Sie mich auf Twitter mit Kommentaren oder Fragen.