Ricerca…


introduzione

async e await costruzione in cima alle promesse e ai generatori per esprimere in linea azioni asincrone. Ciò rende il codice di callback asincrono o molto più semplice da mantenere.

Funziona con la parola chiave async restituisce una Promise e può essere chiamata con quella sintassi.

All'interno di una async function la parola chiave await può essere applicata a qualsiasi Promise e causerà l'esecuzione di tutto il corpo della funzione dopo l' await da eseguire dopo la risoluzione della promessa.

Sintassi

  • funzione async foo () {
    ...
    attendi asyncCall ()
    }
  • funzione asincrona () {...}
  • async () => {...}
  • (async () => {
    const data = attendi asyncCall ()
    console.log (data)}) ()

Osservazioni

Le funzioni asincrone sono uno zucchero sintattico sulle promesse e sui generatori. Ti aiutano a rendere il tuo codice più leggibile, manutenibile, più facile da individuare e con meno livelli di indentazione.

introduzione

Una funzione definita come async è una funzione che può eseguire azioni asincrone ma sembra ancora sincronizzata. Il modo in cui è fatto è usare la parola chiave await per posticipare la funzione mentre attende che una Promessa risolva o rifiuti.

Nota: le funzioni asincrone sono una proposta Stage 4 ("Finished") in pista da includere nello standard ECMAScript 2017.

Ad esempio, utilizzando l' API di recupero basata su promessa:

async function getJSON(url) {
    try {
        const response = await fetch(url);
        return await response.json();
    }
    catch (err) {
        // Rejections in the promise will get thrown here
        console.error(err.message);
    }
}

Una funzione asincrona restituisce sempre una Promessa stessa, quindi puoi usarla in altre funzioni asincrone.

Stile di funzione della freccia

const getJSON = async url => {
    const response = await fetch(url);
    return await response.json();
}

Meno rientranza

Con le promesse:

function doTheThing() {
    return doOneThing()
        .then(doAnother)
        .then(doSomeMore)
        .catch(handleErrors)
}

Con funzioni asincrone:

async function doTheThing() {
    try {
        const one = await doOneThing();
        const another = await doAnother(one);
        return await doSomeMore(another);
    } catch (err) {
        handleErrors(err);
    }
}

Nota come il ritorno è in basso, e non in alto, e usi la meccanica nativa di gestione degli errori del linguaggio ( try/catch ).

Attesa e precedenza degli operatori

È necessario tenere presente la priorità dell'operatore quando si utilizza la parola chiave await .

Immagina di avere una funzione asincrona che chiama un'altra funzione asincrona, getUnicorn() che restituisce una Promessa che si risolve in un'istanza di classe Unicorn . Ora vogliamo ottenere la dimensione dell'unicorno usando il metodo getSize() di quella classe.

Guarda il seguente codice:

async function myAsyncFunction() {
    await getUnicorn().getSize();
}

A prima vista, sembra valido, ma non lo è. A causa della precedenza degli operatori, è equivalente al seguente:

async function myAsyncFunction() {
    await (getUnicorn().getSize());
}

Qui proviamo a chiamare il metodo getSize() dell'oggetto Promise, che non è quello che vogliamo.

Invece, dovremmo usare parentesi per indicare che prima vogliamo aspettare l'unicorno e quindi chiamare il metodo getSize() del risultato:

async function asyncFunction() {
    (await getUnicorn()).getSize();
}

Ovviamente. la versione precedente potrebbe essere valida in alcuni casi, ad esempio se la funzione getUnicorn() era sincrona, ma il metodo getSize() era asincrono.

Funzioni asincrone rispetto a Promesse

async funzioni async non sostituiscono il tipo Promise ; aggiungono parole chiave per le lingue che rendono le promesse più facili da chiamare. Sono intercambiabili:

async function doAsyncThing() { ... }

function doPromiseThing(input) { return new Promise((r, x) => ...); }

// Call with promise syntax
doAsyncThing()
    .then(a => doPromiseThing(a))
    .then(b => ...)
    .catch(ex => ...);

// Call with await syntax
try {
    const a = await doAsyncThing();
    const b = await doPromiseThing(a);
    ...
}
catch(ex) { ... }

Qualsiasi funzione che utilizza catene di promesse può essere riscritta utilizzando await :

function newUnicorn() {
  return fetch('unicorn.json')                     // fetch unicorn.json from server
  .then(responseCurrent => responseCurrent.json()) // parse the response as JSON
  .then(unicorn =>
    fetch('new/unicorn', {                         // send a request to 'new/unicorn' 
        method: 'post',                            // using the POST method
        body: JSON.stringify({unicorn})            // pass the unicorn to the request body
    })
  )
  .then(responseNew => responseNew.json())
  .then(json => json.success)                      // return success property of response
  .catch(err => console.log('Error creating unicorn:', err));
 }

La funzione può essere riscritta usando async / await come segue:

async function newUnicorn() {
  try {
    const responseCurrent = await fetch('unicorn.json'); // fetch unicorn.json from server
    const unicorn = await responseCurrent.json();        // parse the response as JSON
    const responseNew = await fetch('new/unicorn', {     // send a request to 'new/unicorn'
      method: 'post',                                    // using the POST method
      body: JSON.stringify({unicorn})                    // pass the unicorn to the request body
    });
    const json = await responseNew.json();
    return json.success                                  // return success property of response
  } catch (err) {
    console.log('Error creating unicorn:', err);
  }
}

Questo async variante di newUnicorn() appare per restituire un Promise , ma in realtà ci sono stati molteplici await parole chiave. Ognuno ha restituito una Promise , quindi in realtà avevamo una raccolta di promesse piuttosto che una catena.

In effetti, possiamo considerarlo come una function* generatore, e ogni await è una yield new Promise . Tuttavia, i risultati di ogni promessa sono necessari per il prossimo per continuare la funzione. Questo è il motivo per cui è necessaria la parola chiave async aggiuntiva sulla funzione (così come la parola chiave await quando si chiamano le promesse) poiché dice a Javascript di creare automaticamente un osservatore per questa iterazione. La Promise restituita dalla async function newUnicorn() risolve al termine di questa iterazione.

In pratica, non è necessario considerarlo; await nasconde la promessa e async nasconde l'iterazione del generatore.

È possibile chiamare le funzioni async come se fossero promesse e await qualsiasi promessa o funzione async . Non è necessario await una funzione asincrona, proprio come è possibile eseguire una promessa senza .then() .

È anche possibile utilizzare un IIFE async se si desidera eseguire immediatamente tale codice:

(async () => {
  await makeCoffee()
  console.log('coffee is ready!')
})()

Looping con async attendono

Quando si utilizza async nei loop, è possibile che si verifichino alcuni di questi problemi.

Se si tenta semplicemente di utilizzare attendersi all'interno di forEach , verrà generato un errore di Unexpected token .

(async() => {
 data = [1, 2, 3, 4, 5];
 data.forEach(e => {
   const i = await somePromiseFn(e);
   console.log(i);
 });
})();

Questo deriva dal fatto che hai visto erroneamente la funzione della freccia come un blocco. L' await sarà nel contesto della funzione di callback, che non è async .
L'interprete ci protegge dall'errore di cui sopra, ma se aggiungi async al richiamo di forEach nessun errore viene generato. Potresti pensare che questo risolva il problema, ma non funzionerà come previsto.

Esempio:

(async() => {
  data = [1, 2, 3, 4, 5];
  data.forEach(async(e) => {
    const i = await somePromiseFn(e);
    console.log(i);
  });
  console.log('this will print first');
})();

Ciò accade perché la funzione async di callback può solo sospendere se stessa, non la funzione asincrona genitore.

Potresti scrivere una funzione asyncForEach che restituisce una promessa e quindi potresti await asyncForEach(async (e) => await somePromiseFn(e), data ) qualcosa come await asyncForEach(async (e) => await somePromiseFn(e), data ) In await asyncForEach(async (e) => await somePromiseFn(e), data ) si restituisce una promessa che si risolve quando tutte le callback sono attese e completate. Ma ci sono modi migliori per farlo, e basta usare un ciclo.

È possibile utilizzare un ciclo for-of o un ciclo for/while , non importa quale si sceglie.

(async() => {
  data = [1, 2, 3, 4, 5];
  for (let e of data) {
    const i = await somePromiseFn(e);
    console.log(i);
  }
  console.log('this will print last');
})();

Ma c'è un altro problema. Questa soluzione attenderà ogni chiamata a somePromiseFn da completare prima di iterare su quella successiva.
Questo è ottimo se vuoi che le somePromiseFn invocazioni somePromiseFn siano eseguite in ordine, ma se vuoi che vengano eseguite contemporaneamente, dovrai await su Promise.all .

(async() => {
 data = [1, 2, 3, 4, 5];
 const p = await Promise.all(data.map(async(e) => await somePromiseFn(e)));
 console.log(...p);
})();

Promise.all riceve una serie di promesse come unico parametro e restituisce una promessa. Quando tutte le promesse nell'array vengono risolte, anche la promessa restituita viene risolta. await questa promessa e quando sarà risolta saranno disponibili tutti i nostri valori.

Gli esempi precedenti sono completamente eseguibili. La funzione somePromiseFn può essere eseguita come una funzione di eco asincrono con un timeout. Puoi provare gli esempi in babel-repl con almeno il preset stage-3 e guardare l'output.

function somePromiseFn(n) {
 return new Promise((res, rej) => {
   setTimeout(() => res(n), 250);
 });
}

Operazioni simultanee asincrone (parallele)

Spesso si desidera eseguire operazioni asincrone in parallelo. V'è la sintassi diretta che supporta questa nel async / await proposta, ma dal momento che await aspetterà una promessa, si può avvolgere più promesse insieme a Promise.all aspettare per loro:

// Not in parallel

async function getFriendPosts(user) {
    friendIds = await db.get("friends", {user}, {id: 1});
    friendPosts = [];
    for (let id in friendIds) {
        friendPosts = friendPosts.concat( await db.get("posts", {user: id}) );
    }
    // etc.
}

Questo farà ogni query per ottenere in serie i messaggi di ciascun amico, ma possono essere eseguiti contemporaneamente:

// In parallel

async function getFriendPosts(user) {
    friendIds = await.db.get("friends", {user}, {id: 1});
    friendPosts = await Promise.all( friendIds.map(id => 
      db.get("posts", {user: id})
    );
    // etc.
}

Questo passerà sopra l'elenco di ID per creare una serie di promesse. await attenderà che tutte le promesse siano complete. Promise.all li unisce in un'unica promessa, ma sono fatti in parallelo.



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow