Asynchrone Programmierung in Javascript: Verstehen Sie Promise wirklich?

Asynchrone Programmierung in Javascript: Verstehen Sie Promise wirklich?

Vorwort

Bei der asynchronen Programmierung spielt Promise eine wichtige Rolle und ist sinnvoller und leistungsfähiger als herkömmliche Lösungen (Rückruffunktionen und Ereignisse). Einige von Ihnen stellen sich möglicherweise diese Frage: Wir schreiben das Jahr 2020, warum sprechen wir immer noch über Promise? Tatsächlich scheinen einige Freunde diesen „alten Freund“, mit dem sie fast täglich zu tun haben, zu verstehen, aber wenn sie tiefer graben, haben sie möglicherweise viele Fragen. Dieser Artikel wird Ihnen helfen, diesen vertrauten Fremden – Promise – besser zu verstehen.

Grundlegende Verwendung

Grammatik

neues Promise(Funktion(auflösen, ablehnen) {...} /* Ausführender */)
  • Beim Erstellen eines Promise-Objekts müssen Sie eine Executor-Funktion übergeben, und die wichtigsten Geschäftsprozesse werden in der Executor-Funktion ausgeführt.
  • Wenn der Promise-Konstruktor ausgeführt wird, wird sofort die Executor-Funktion aufgerufen. Die Funktionen „Resolve“ und „Reject“ werden als Parameter an den Executor übergeben. Wenn die Funktionen „Resolve“ und „Reject“ aufgerufen werden, wird der Status des Promise in „erfüllt“ bzw. „abgelehnt“ geändert. Sobald sich der Status ändert, ändert er sich nicht mehr und das Ergebnis kann jederzeit abgerufen werden.
  • Wenn die Resolve-Funktion in der Executor-Funktion aufgerufen wird, wird die durch promise.then festgelegte Rückruffunktion ausgelöst; und wenn die Reject-Funktion aufgerufen wird, wird die durch promise.catch festgelegte Rückruffunktion ausgelöst.

Es ist erwähnenswert, dass Promise zur Verwaltung asynchroner Programmierung verwendet wird. Es ist selbst nicht asynchron. Wenn ein neues Promise erstellt wird, wird die Executor-Funktion sofort ausgeführt. Normalerweise verarbeiten wir jedoch einen asynchronen Vorgang in der Executor-Funktion. Im folgenden Code wird beispielsweise am Anfang eine 2 ausgedruckt.

lass p1 = neues Versprechen(()=>{
    setzeTimeout(()=>{
      console.log(1)
    },1000)
    console.log(2)
  })
console.log(3) // 2 3 1

Promise verwendet die Technologie der verzögerten Bindung von Rückruffunktionen. Wenn die Resolve-Funktion ausgeführt wird, ist die Rückruffunktion noch nicht gebunden, sodass die Ausführung der Rückruffunktion nur verschoben werden kann. Was bedeutet das genau? Schauen wir uns das folgende Beispiel an:

let p1 = neues Versprechen((lösen,ablehnen)=>{
  konsole.log(1);
  lösen('Boot in den Wellen')
  console.log(2)
})
// dann: Legen Sie die Methode für die Nachbearbeitung bei Erfolg oder Misserfolg fest p1.then(result=>{
 //p1 verzögerte Bindungsrückruffunktion console.log('Erfolg'+Ergebnis)
},Grund=>{
  console.log('fehlgeschlagen' + Grund)
})
console.log(3)
// 1
// 2
// 3
// Erfolg in den Wellen

Wenn ein neues Promise erstellt wird, wird zuerst die Executor-Funktion ausgeführt und 1 und 2 werden gedruckt. Wenn das Promise aufgelöst ist, wird die Mikrotask ausgelöst oder die synchrone Aufgabe fortgesetzt.

Wenn p1.then ausgeführt wird, werden zwei Funktionen gespeichert (diese beiden Funktionen wurden zu diesem Zeitpunkt noch nicht ausgeführt) und dann 3 gedruckt. Zu diesem Zeitpunkt ist die Synchronisierungsaufgabe abgeschlossen und schließlich wird die Mikrotask ausgeführt, wodurch die erfolgreiche Methode in .then ausgeführt wird.

Fehlerbehandlung

Der Fehler des Promise-Objekts hat einen „sprudelnden“ Charakter und wird zurückgegeben, bis er von der Funktion „onReject“ verarbeitet oder von der Catch-Anweisung erfasst wird. Mit dieser „Bubbling“-Funktion ist es nicht erforderlich, Ausnahmen in jedem Promise-Objekt separat abzufangen.

Um auf ein „then“ zu stoßen, führen Sie die Erfolgs- oder Fehlermethode aus. Wenn diese Methode jedoch im aktuellen „then“ nicht definiert ist, wird sie auf die nächste entsprechende Funktion verschoben.

Funktion Executor (auflösen, ablehnen) {
  let rand = Math.random()
  console.log(1)
  console.log(rand)
  wenn (rand > 0,5) {
    lösen()
  } anders {
    ablehnen()
  }
}
var p0 = neues Versprechen (Vollstrecker)
var p1 = p0.then((Wert) => {
  console.log('erfolgreich-1')
  gib neues Promise(Vollstrecker) zurück
})
var p2 = p1.then((Wert) => {
  console.log('erfolgreich-2')
  gib neues Promise(Vollstrecker) zurück
})
p2.catch((Fehler) => {
  console.log('Fehler', Fehler)
})
console.log(2)

Dieser Code hat drei Promise-Objekte: p0~p2. Unabhängig davon, welches Objekt eine Ausnahme auslöst, kann das letzte Objekt p2.catch verwendet werden, um die Ausnahme abzufangen. Auf diese Weise können Fehler aller Promise-Objekte zur Verarbeitung in einer Funktion zusammengeführt werden, wodurch das Problem gelöst wird, dass jede Aufgabe Ausnahmen separat behandeln muss.

Auf diese Weise vermeiden wir verschachtelte Aufrufe und häufige Fehlerbehandlungen, wodurch der von uns geschriebene Code eleganter wird und mehr dem linearen Denken des Menschen entspricht.

Promise-Chain-Aufruf

Wir alle wissen, dass wir mehrere Promises miteinander verketten können, um eine Reihe verschiedener Schritte darzustellen. Der Schlüssel zu diesem Ansatz sind die folgenden zwei inhärenten Verhaltensweisen von Promises:

  • Jedes Mal, wenn Sie dann ein Promise aufrufen, wird ein neues Promise erstellt und zurückgegeben, das wir verketten können.
  • Welcher Wert auch immer vom Erfüllungs-Rückruf (erstes Argument) des dann folgenden Aufrufs zurückgegeben wird, er wird automatisch als Erfüllung des verketteten Versprechens (im ersten Punkt) festgelegt.

Lassen Sie uns zunächst anhand des folgenden Beispiels die Bedeutung dieses Satzes erklären und dann den Ausführungsprozess des Kettenaufrufs im Detail vorstellen.

let p1 = neues Versprechen((lösen, ablehnen) => {
    resolve(100) // bestimmt die nächste erfolgreiche Methode im then, die ausgeführt werden soll})
// Mit p1 verbinden
sei p2=p1.dann(Ergebnis=>{
    console.log('Erfolg 1 '+Ergebnis)
    Rückgabewert Promise.reject(1) 
//Gibt eine neue Promise-Instanz zurück, die feststellt, dass die aktuelle Instanz ein Fehler ist, sodass die nächste Methode ausgeführt wird}, reason=>{
    console.log('Fehler 1 '+Grund)
    Rückgabe 200
})
// Mit p2 verbinden 
sei p3=p2.dann(Ergebnis=>{
    console.log('Erfolg 2' + Ergebnis)
},Grund=>{
    console.log('Fehler 2 '+Grund)
})
// Erfolg 1 100
// Fehler 2 1

Wir erfüllen das durch den ersten Aufruf erstellte und zurückgegebene Versprechen p2, indem wir dann Promise.reject(1) zurückgeben. Der dann ausgeführte Aufruf von p2 erhält den Erfüllungswert aus der Anweisung return Promise.reject(1). Natürlich erstellt p2.then ein weiteres neues Promise, das in der Variable p3 gespeichert werden kann.

Der Erfolg oder Misserfolg der von new Promise generierten Instanz hängt davon ab, ob die Auflösung oder Ablehnung bei der Ausführung der Executor-Funktion ausgeführt wird oder ob während der Ausführung der Executor-Funktion ein abnormaler Fehler auftritt. In beiden Fällen wird der Instanzstatus auf „Fehlschlag“ geändert.

Der Status der neuen Instanz, den p2 bei der Ausführung zurückgibt, bestimmt, welche Methode im nächsten ausgeführt wird. Es gibt mehrere Situationen:

  • Unabhängig davon, ob es sich um eine erfolgreiche oder fehlgeschlagene Methodenausführung (die beiden Methoden in then) handelt, wird der Instanzstatus in „Fehlgeschlagen“ geändert, wenn während der Ausführung eine Ausnahme ausgelöst wird.
  • Wenn in der Methode eine neue Promise-Instanz zurückgegeben wird (wie etwa Promise.reject(1) im obigen Beispiel), ist das Ergebnis der Rückgabe dieser Instanz Erfolg oder Misserfolg, was auch bestimmt, ob die aktuelle Instanz erfolgreich ist oder fehlschlägt.
  • In den übrigen Fällen geht es grundsätzlich darum, die Instanz in einen erfolgreichen Zustand zu versetzen und das von der vorherigen Then-Methode zurückgegebene Ergebnis an die nächste Then-Methode zu übergeben.

Schauen wir uns ein weiteres Beispiel an

neues Versprechen(resolve=>{
    resolve(a) // Fehler // Bei der Ausführung dieser Executor-Funktion tritt ein Ausnahmefehler auf, der dazu führt, dass die nächste, dann fehlerhafte Methode ausgeführt wird}).then(result=>{
    console.log(`Erfolg: ${result}`)
    Ergebnis zurückgeben*10
},Grund=>{
    console.log(`Fehlgeschlagen: ${reason}`)
// Bei der Ausführung dieses Satzes tritt keine Ausnahme auf oder es wird eine fehlgeschlagene Promise-Instanz zurückgegeben, sodass die nächste, dann erfolgreiche Methode ausgeführt wird // Hier gibt es keine Rückgabe und am Ende wird undefined zurückgegeben
}).dann(Ergebnis=>{
    console.log(`Erfolg: ${result}`)
},Grund=>{
    console.log(`Fehlgeschlagen: ${reason}`)
})
// Fehler: Referenzfehler: a ist nicht definiert
// Erfolg: undefiniert

asynchron und warten

Aus den obigen Beispielen können wir ersehen, dass die Verwendung von Promise das Problem der Callback-Hölle zwar sehr gut lösen kann, diese Methode jedoch voller then()-Methoden von Promise ist. Wenn der Verarbeitungsfluss komplizierter ist, ist der gesamte Code voller then, die Semantik ist nicht offensichtlich und der Code kann den Ausführungsfluss nicht gut darstellen.

Die Implementierung von async/await, einer neuen asynchronen Programmiermethode, die in ES7 hinzugefügt wurde, basiert auf Promise. Einfach ausgedrückt gibt die async-Funktion ein Promise-Objekt zurück, das die Syntax des Generators darstellt. Viele Leute denken, dass async/await die ultimative Lösung für asynchrone Vorgänge ist:

  • Die Syntax ist prägnant, ähnelt eher synchronem Code und entspricht eher den üblichen Lesegewohnheiten.
  • Verbessern Sie die Code-Organisationsmethode für die serielle Ausführung asynchroner Operationen in JS, um die Verschachtelung von Rückrufen zu reduzieren.
  • Sie können try/catch nicht zur Fehlererfassung in Promise anpassen, aber Sie können Fehler in Async/await wie synchronen Code behandeln.

Es gibt jedoch auch einige Nachteile, da await asynchronen Code in synchronen Code umwandelt. Wenn mehrere asynchrone Codes keine Abhängigkeiten haben, aber await verwenden, wird die Leistung reduziert.

asynchroner Funktionstest () {
  // Wenn der folgende Code keine Abhängigkeiten hat, können Sie Promise.all verwenden. // Wenn Abhängigkeiten vorhanden sind, ist dies tatsächlich ein Beispiel für die Lösung von Callback Hell await fetch(url1).
  warte auf Abruf (URL2)
  warte auf Abruf (URL3)
}

Können Sie anhand des folgenden Codes erkennen, was gedruckt wird?

lass p1 = Promise.resolve(1)
lass p2 = neues Versprechen(auflösen => {
  setzeTimeout(() => {
    Entschlossenheit(2)
  }, 1000)
})
asynchrone Funktion fn() {
  console.log(1)
// Wenn der Code bis zu dieser Zeile ausgeführt wird (diese Zeile an den Anfang setzen), wird eine asynchrone Mikrotask erstellt // Auf die Versprechen warten, das Ergebnis zurückzugeben, und der Code unter await wird auch in der Task-Warteschlange aufgeführt let result1 = await p2
  console.log(3)
  lass result2 = warte auf p1
  console.log(4)
}
fn()
console.log(2)
// 1 2 3 4

Wenn die auf der rechten Seite von „await“ ausgedrückte Logik ein Versprechen ist, wartet „await“ auf das Rückgabeergebnis des Versprechens. Erst wenn der zurückgegebene Status aufgelöst ist, wird das Ergebnis zurückgegeben. Wenn das Versprechen fehlgeschlagen ist, erhält „await“ sein Rückgabeergebnis nicht und der Code unter „await“ wird nicht weiter ausgeführt.

sei p1 = Promise.reject(100)
asynchrone Funktion fn1() {
  lass Ergebnis = warte auf p1
  console.log(1) //Diese Codezeile wird nicht ausgeführt}

Schauen wir uns eine komplexere Frage an:

console.log(1)
setTimeout(()=>{console.log(2)},1000)
asynchrone Funktion fn(){
    console.log(3)
    setTimeout(()=>{console.log(4)},20)
    returniere Promise.reject()
}
asynchrone Funktion run() {
    console.log(5)
    warte auf fn()
    konsole.log(6)
}
laufen()
//Die Ausführung dauert etwa 150 ms for(let i=0;i<90000000;i++){}
setzeTimeout(()=>{
    konsole.log(7)
    neues Versprechen(resolve=>{
        konsole.log(8)
        lösen()
    }).then(()=>{console.log(9)})
},0)
konsole.log(10)
// 1 5 3 10 4 7 8 9 2

Bevor Sie diese Frage beantworten, müssen die Leser Folgendes verstehen:

  • Zu den auf Mikrotasks basierenden Technologien gehören MutationObserver, Promise und viele andere auf Promise basierende Technologien. In dieser Frage sind sowohl resolve() als auch await fn() Mikrotasks.
  • Unabhängig davon, ob die Makroaufgabe eingetroffen ist oder in welcher Reihenfolge sie platziert wird, priorisiert die Engine jedes Mal, wenn der Ausführungsstapel des Hauptthreads leer ist, die Mikrotask-Warteschlange, verarbeitet alle Aufgaben in der Mikrotask-Warteschlange und verarbeitet dann die Makroaufgabe.

Als nächstes analysieren wir Schritt für Schritt:

  • Führen Sie zuerst den synchronen Code aus, geben Sie 1 aus, stoßen Sie auf das erste setTimeout, stellen Sie seinen Rückruf in die Aufgabenwarteschlange (Makroaufgabe) und führen Sie die Ausführung weiter aus
  • Führen Sie „run()“ aus, geben Sie 5 aus und setzen Sie die Ausführung fort. Treffen Sie „await fn()“ und stellen Sie es in die Aufgabenwarteschlange (Mikrotask).
  • await fn() Wenn die aktuelle Codezeile ausgeführt wird, wird die fn-Funktion sofort ausgeführt und gibt 3 aus. Wenn sie auf das zweite setTimeout trifft, wird ihr Rückruf in die Taskwarteschlange (Makrotask) gestellt. Der Code unter await fn() muss auf die Rückgabe des Promise-Erfolgsstatus warten, bevor er ausgeführt wird, daher wird 6 nicht ausgegeben.
  • Bei fortgesetzter Ausführung tritt der Synchronisierungscode für die For-Schleife auf. Es müssen 150 ms gewartet werden. Obwohl das zweite setTimeout die Zeit erreicht hat, wird es nicht ausgeführt. Beim dritten setTimeout wird dessen Rückruf in die Aufgabenwarteschlange (Makroaufgabe) gestellt und dann 10 ausgedruckt. Es ist anzumerken, dass eine Zeitverzögerung von 0 Millisekunden eigentlich nicht erreichbar ist. Gemäß dem HTML5-Standard beträgt die Mindestverzögerung für die Ausführung von setTimeOut 4 Millisekunden.
  • Wenn nach der Ausführung des Synchronisationscodes zu diesem Zeitpunkt keine Mikrotask vorhanden ist, wird die Makrotask ausgeführt. Das oben erwähnte setTimeout wird zuerst ausgeführt und 4 gedruckt.
  • Dann wird die nächste Makroaufgabe „setTimeout“ ausgeführt, sodass zuerst 7 ausgegeben wird. Wenn „new Promise“ ausgeführt wird, wird die Executor-Funktion sofort ausgeführt und 8 ausgegeben. Wenn dann „resolve“ ausgeführt wird, wird die Mikroaufgabe ausgelöst, sodass 9 ausgegeben wird.
  • Schließlich wird die erste setTimeout-Makroaufgabe ausgeführt und druckt 2

Häufig verwendete Methoden

1. Versprechen.lösen()

Die Methode Promise.resolve(value) gibt ein Promise-Objekt zurück, das mit dem angegebenen Wert aufgelöst wird.
Promise.resolve() entspricht Folgendem:

Versprechen.lösen('foo')
// Entspricht neuem Promise(resolve => resolve('foo'))

Die Parameter der Methode Promise.resolve sind in vier Fälle unterteilt.

(1) Der Parameter ist eine Promise-Instanz

Wenn der Parameter eine Promise-Instanz ist, gibt Promise.resolve die Instanz unverändert und ohne Änderungen zurück.

const p1 = neues Versprechen (Funktion (auflösen, ablehnen) {
  setTimeout(() => ablehnen(neuer Fehler('fehlgeschlagen')), 3000)
})
const p2 = neues Versprechen (Funktion (auflösen, ablehnen) {
  setTimeout(() => auflösen(p1), 1000)
})
Teil 2
  .then(Ergebnis => console.log(Ergebnis))
  .catch(Fehler => console.log(Fehler))
// Fehler: fehlgeschlagen

Im obigen Code ist p1 ein Versprechen, das nach 3 Sekunden abgelehnt wird. Der Status von p2 ändert sich nach 1 Sekunde und die Resolve-Methode gibt p1 zurück. Da p2 ein anderes Promise zurückgibt, wird der Zustand von p2 selbst ungültig und der Zustand von p2 wird durch den Zustand von p1 bestimmt. Daher zielen alle nachfolgenden „then“-Anweisungen auf Letzteres ab (S. 1). Nach weiteren 2 Sekunden wird p1 abgelehnt und die von der Catch-Methode angegebene Rückruffunktion ausgelöst.

(2) Der Parameter ist kein Objekt mit einer then-Methode oder überhaupt kein Objekt

Promise.resolve("Erfolg").dann(Funktion(Wert) {
 // Die Parameter der Methode Promise.resolve werden gleichzeitig an die Callback-Funktion übergeben.
  console.log(Wert); // "Erfolgreich"
}, Funktion(Wert) {
  // wird nicht aufgerufen });

(3) Ohne Parameter

Die Methode Promise.resolve() ermöglicht Ihnen den Aufruf ohne Parameter und gibt direkt ein Promise-Objekt im aufgelösten Zustand zurück. Wenn Sie ein Promise-Objekt erhalten möchten, ist der direkte Aufruf der Methode Promise.resolve() bequemer.

Versprechen.auflösen().dann(Funktion () {
  console.log('zwei');
});
konsole.log('eins');
// eins zwei

(4) Der Parameter ist ein thenable-Objekt

Ein thenable-Objekt verweist auf ein Objekt mit einer then-Methode. Die Methode Promise.resolve konvertiert dieses Objekt in ein Promise-Objekt und führt dann sofort die then-Methode des thenable-Objekts aus.

lass dann = {
  dann: Funktion(auflösen, ablehnen) {
    Entschlossenheit (42);
  }
};
lass p1 = Promise.resolve(thenable);
p1.dann(Funktion(Wert) {
  console.log(Wert); // 42
});

2. Versprechen.ablehnen()

Die Methode Promise.reject() gibt ein Promise-Objekt mit einem Ablehnungsgrund zurück.

neues Versprechen((lösen,ablehnen) => {
    ablehnen(neuer Fehler("Fehler"));
});
//Entspricht Promise.reject(new Error("Error"));  

// Verwendungsmethode Promise.reject(new Error("BOOM!")).catch(error => {
    konsole.fehler(fehler);
});

Es ist erwähnenswert, dass nach dem Aufruf von „Resolve“ oder „Reject“ die Mission von Promise abgeschlossen ist und nachfolgende Vorgänge in der then-Methode platziert werden sollten und nicht direkt nach „Resolve“ oder „Reject“ geschrieben werden sollten. Daher ist es besser, ihnen eine Return-Anweisung voranzustellen, damit es keine Überraschungen gibt.

neues Versprechen((lösen, ablehnen) => {
  Rückgabe ablehnen(1);
  // Die folgenden Anweisungen werden nicht ausgeführt console.log(2);
})

3. Versprechen.alles()

lass p1 = Promise.resolve(1)
lass p2 = neues Versprechen(auflösen => {
  setzeTimeout(() => {
    Entschlossenheit(2)
  }, 1000)
})
lass p3 = Promise.resolve(3)
Versprechen.alles([p3, p2, p1])
  .dann(Ergebnis => {
 // Die Ergebnisse werden in der Reihenfolge zurückgegeben, in der die Instanzen in das Array geschrieben werden console.log(result) // [ 3, 2, 1 ]
  })
  .catch(Grund => {
    console.log("fehlgeschlagen:Grund")
  })

Promise.all generiert und gibt ein neues Promise-Objekt zurück, sodass es alle Methoden der Promise-Instanz verwenden kann. Diese Methode wird zurückgegeben, wenn alle als Parameter übergebenen Promise-Objekte im Promise-Array aufgelöst wurden und das neu erstellte Promise die Werte dieser Promises verwendet.

Wenn eines der Promises in den Parametern abgelehnt wird, wird der gesamte Promise.all-Aufruf sofort beendet und ein neues abgelehntes Promise-Objekt zurückgegeben.

4. Versprechen.allSettled()

Manchmal interessieren uns die Ergebnisse asynchroner Vorgänge nicht, sondern nur, ob diese Vorgänge abgeschlossen sind. Derzeit ist die von ES2020 eingeführte Methode Promise.allSettled() sehr nützlich. Ohne diese Methode wäre es schwierig sicherzustellen, dass alle Vorgänge abgeschlossen werden. Dies ist mit der Methode Promise.all() nicht möglich.

Angenommen, es gibt ein solches Szenario: Eine Seite hat drei Bereiche, die drei unabhängigen Schnittstellendaten entsprechen, und Promise.all wird verwendet, um die drei Schnittstellen gleichzeitig anzufordern. Wenn eine der Schnittstellen eine Ausnahme aufweist, lautet der Status „Abgelehnt“, was dazu führt, dass die Daten der drei Bereiche auf der Seite nicht ausgegeben werden. Diese Situation ist für uns offensichtlich inakzeptabel. Das Aufkommen von Promise.allSettled kann dieses Problem lösen:

Versprechen.allSettled([
  Promise.reject({ Code: 500, Nachricht: 'Serviceausnahme' }),
  Versprechen.resolve({ Code: 200, Liste: [] }),
  Versprechen.resolve({ Code: 200, Liste: [] })
]).dann(res => {
  Konsole.log(res)
  /*
    0: {Status: "abgelehnt", Grund: {…}}
    1: {Status: "erfüllt", Wert: {…}}
    2: {Status: "erfüllt", Wert: {…}}
  */
  // Den abgelehnten Status herausfiltern und sicherstellen, dass so viele Seitenbereichsdaten wie möglich vorliegen RenderContent(
    res.filter(el => {
      return el.status !== 'abgelehnt'
    })
  )
})

Promise.allSettled ist Promise.all ähnlich. Es nimmt ein Array von Promises als Parameter und gibt ein neues Promise zurück. Der einzige Unterschied besteht darin, dass es keinen Kurzschluss gibt. Das heißt, wenn alle Promises verarbeitet sind, können wir den Status jedes Promises abrufen, unabhängig davon, ob es erfolgreich verarbeitet wurde.

5. Versprechen.race()

Die Auswirkung der Methode Promise.all() ist: „Wer langsamer läuft, führt den Rückruf aus.“ Daher gibt es eine andere Methode, die der Aussage „Wer schneller läuft, führt den Rückruf aus“ entspricht: die Methode Promise.race(). Das Wort bedeutet ursprünglich „Rennen“. Die Verwendung von „Race“ ist die gleiche wie bei „All“, wobei ein Array von Promise-Objekten als Parameter empfangen wird.

Promise.all wird die nachfolgende Verarbeitung erst fortsetzen, wenn alle empfangenen Objekt-Promises erfüllt oder abgelehnt wurden. Im Gegensatz dazu wird Promise.race die nachfolgende Verarbeitung fortsetzen, solange ein Promise-Objekt den Status „Erfüllt“ oder „Abgelehnt“ erreicht.

// Führe die Auflösung nach `delay` Millisekunden aus
Funktion TimerPromisify(Verzögerung) {
    gib ein neues Versprechen zurück (Auflösen => {
        setzeTimeout(() => {
            Lösung (Verzögerung);
        }, Verzögerung);
    });
}
// Wenn ein Versprechen eingelöst oder abgelehnt wird, beendet das Programm die Ausführung von Promise.race([
    TimerPromisefy(1),
    TimerVersprechen(32),
    TimerVersprechen(64)
]).dann(Funktion (Wert) {
    console.log(Wert); // => 1
});

Der obige Code erstellt drei Promise-Objekte, die jeweils nach 1 ms, 32 ms und 64 ms bestätigt werden, d. h. FulFilled, und die von .then registrierte Rückruffunktion wird 1 ms nach der Bestätigung des ersten Objekts aufgerufen.

6. Versprechen.prototype.finally()

ES9 fügt eine finally()-Methode hinzu, die ein Promise zurückgibt. Am Ende des Versprechens, unabhängig davon, ob es erfüllt oder abgelehnt wird, wird die angegebene Rückruffunktion ausgeführt. Auf diese Weise kann Code übergeben werden, der ausgeführt werden muss, unabhängig davon, ob das Versprechen erfolgreich erfüllt wurde oder nicht. Dadurch wird die Situation vermieden, dass dieselbe Anweisung einmal sowohl in then() als auch in catch() geschrieben werden muss.

Beispielsweise wird vor dem Senden einer Anfrage ein Ladebildschirm angezeigt. Nachdem die Anfrage gesendet wurde, hoffen wir, den Ladebildschirm ausschalten zu können, unabhängig davon, ob die Anfrage einen Fehler enthält oder nicht.

dies.laden = wahr
Anfrage()
  .then((res) => {
    // etwas tun
  })
  .fangen(() => {
    // Fehler protokollieren
  })
  .finally(() => {
    dies.laden = falsch
  })

Die Rückruffunktion der Finally-Methode akzeptiert keine Parameter, was darauf hinweist, dass die Vorgänge in der Finally-Methode unabhängig vom Status sein sollten und nicht vom Ausführungsergebnis von Promise abhängen.

Praktische Anwendung

Angenommen, es besteht eine solche Anforderung: Das rote Licht leuchtet alle 3 Sekunden einmal auf, das grüne Licht leuchtet alle 1 Sekunde einmal und das gelbe Licht leuchtet alle 2 Sekunden einmal auf. Wie können wir dafür sorgen, dass die drei Lichter abwechselnd und wiederholt aufleuchten?
Drei Lichtfunktionen sind bereits vorhanden:

Funktion rot() {
    konsole.log('rot');
}
Funktion grün() {
    konsole.log('grün');
}
Funktion gelb() {
    konsole.log('gelb');
}

Die Komplexität dieses Problems liegt in der Notwendigkeit, „abwechselnd und wiederholt“ zu leuchten, anstatt eines einmaligen Deals, der nach einmaligem Leuchten endet. Dies können wir durch Rekursion erreichen:

// Implementierung mit Versprechen let task = (timer, light) => {
  returniere neues Promise((lösen, ablehnen) => {
    setzeTimeout(() => {
      wenn (Licht === 'rot') {
        Rot()
      }
      wenn (Licht === 'grün') {
        Grün()
      }
      wenn (Licht === 'gelb') {
        Gelb()
      }
      lösen()
    }, Zeitgeber);
  })
}
lass Schritt = () => {
  Aufgabe (3000, „rot“)
    .then(() => Aufgabe(1000, 'grün'))
    .then(() => Aufgabe(2000, 'gelb'))
    .dann(Schritt)
}
Schritt()

Dasselbe kann auch durch async/await erreicht werden:

// async/await-Implementierung let step = async () => {
  warte auf Aufgabe (3000, „rot“)
  warte auf Aufgabe (1000, „grün“)
  warte auf Aufgabe (2000, „gelb“)
  Schritt()
}
Schritt()

Mit async/await können Sie asynchronen Code im Stil von synchronem Code schreiben. Es besteht kein Zweifel, dass die async/await-Lösung intuitiver ist, aber ein tiefes Verständnis von Promise ist die Grundlage für die Beherrschung von async/await.

Oben sind die Details der asynchronen Programmierung in Javascript aufgeführt. Weitere Informationen zur asynchronen Programmierung in Javascript finden Sie in den anderen verwandten Artikeln auf 123WORDPRESS.COM!

Das könnte Sie auch interessieren:
  • js Promise-Methode zur gleichzeitigen Steuerung
  • JavaScript verwendet Promise zur Verarbeitung mehrerer wiederholter Anfragen
  • So verwenden Sie Promise in JavaScript, um die Anzahl gleichzeitiger Anfragen zu steuern
  • JS asynchroner Code Unit-Test Magie Promise
  • Fragen zum Vorstellungsgespräch zu JS 9 Promise
  • So fügen Sie in JS eine Abbruchfunktion zu einem Versprechen hinzu
  • JS Asynchronous Stack Tracing: Warum „await“ besser ist als „Promise“
  • Detaillierte Erklärung von JavaScript Promise und Async/Await
  • Front-End-JavaScript-Versprechen

<<:  Zusammenfassung der bei der Arbeit häufig verwendeten Linux-Befehle

>>:  So konfigurieren Sie eine JDK-Umgebung unter Linux

Artikel empfehlen

So implementieren Sie die Seiten-Screenshot-Funktion in JS

„Seiten-Screenshot“ ist eine Anforderung, die häu...

img usemap Attribut China Karte Link

HTML-img-Tag: definiert ein Bild, das in eine Webs...

Zusammenfassung der verschiedenen Haltungen der MySQL-Berechtigungseskalation

Inhaltsverzeichnis 1. Schreiben Sie Webshell in d...

So erstellen Sie eine PHP+Nginx+Swoole+MySQL+Redis-Umgebung mit Docker

Betriebssystem: Alibaba Cloud ESC-Instanz centos7...

Vue3-Zeitstempelkonvertierung (ohne Verwendung von Filtern)

Wenn vue2 Zeitstempel konvertiert, verwendet es i...

Detaillierte Schritte zum Einrichten des Hosts Nginx + Docker WordPress Mysql

Umfeld Linux 3.10.0-693.el7.x86_64 Docker-Version...

Installation und Verwendung der Ubuntu 18.04 Serverversion (Bild und Text)

1 Schritte zur Systeminstallation Betriebssystemv...

So aktivieren Sie den Fernzugriff in Docker

Docker-Daemon-Socket Der Docker-Daemon kann über ...

Wissen Sie, wie viele Verbindungen ein Linux-Server verarbeiten kann?

Vorwort Sehen wir uns zunächst an, wie eine TCP-V...

Einfaches Beispiel zum Hinzufügen und Entfernen von HTML-Knoten

Einfaches Beispiel für das Hinzufügen und Entfern...