Zoeken…


Invoering

async en await bouwen bovenop beloften en generatoren om asynchrone acties inline uit te drukken. Dit maakt asynchrone of callback-code veel eenvoudiger te onderhouden.

Functies met het async trefwoord geven een Promise terug en kunnen met die syntax worden opgeroepen.

Binnen een async function het sleutelwoord await op elke Promise worden toegepast en zal het hele lichaam van de functie na het await worden uitgevoerd nadat de belofte is opgelost.

Syntaxis

  • async functie foo () {
    ...
    wacht op asyncCall ()
    }
  • async-functie () {...}
  • async () => {...}
  • (async () => {
    const data = await asyncCall ()
    console.log (data)}) ()

Opmerkingen

Async-functies zijn een syntactische suiker boven beloftes en generatoren. Ze helpen u om uw code leesbaarder, onderhoudbaarder te maken, gemakkelijker om fouten op te vangen en met minder inspringingsniveaus.

Invoering

Een functie die als async is gedefinieerd, is een functie die asynchrone acties kan uitvoeren, maar er toch synchroon uitziet. De manier waarop het is gedaan, is het await sleutelwoord om de functie uit te stellen terwijl het wacht op een belofte om op te lossen of te weigeren.

Opmerking: Async-functies zijn een voorstel van Stage 4 ("Finished") op schema dat moet worden opgenomen in de ECMAScript 2017-standaard.

Bijvoorbeeld met behulp van de op belofte gebaseerde Fetch API :

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);
    }
}

Een async-functie retourneert altijd een belofte zelf, zodat u deze in andere asynchrone functies kunt gebruiken.

Pijl functie stijl

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

Minder inspringen

Met beloften:

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

Met async-functies:

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

Merk op hoe het rendement onderaan staat, en niet bovenaan, en u de native foutafhandelingsmechanismen van de taal gebruikt ( try/catch ).

Wachten en voorrang van operator

U moet rekening houden met de prioriteit van de operator bij het gebruik van await trefwoord.

Stel je voor dat we een asynchrone functie hebben die een andere asynchrone functie getUnicorn() , getUnicorn() die een belofte retourneert die wordt getUnicorn() in een instantie van klasse Unicorn . Nu willen we de grootte van de eenhoorn krijgen met de methode getSize() van die klasse.

Bekijk de volgende code:

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

Op het eerste gezicht lijkt het geldig, maar dat is het niet. Vanwege operatorprioriteit is dit gelijk aan het volgende:

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

Hier proberen we de methode getSize() van het Promise-object aan te roepen, wat niet is wat we willen.

In plaats daarvan moeten we haakjes gebruiken om aan te geven dat we eerst willen wachten op de eenhoorn en vervolgens de methode getSize() van het resultaat aanroepen:

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

Natuurlijk. de vorige versie zou in sommige gevallen geldig kunnen zijn, bijvoorbeeld als de functie getUnicorn() synchroon was, maar de methode getSize() asynchroon was.

Async-functies in vergelijking met Beloften

async functies vervangen het Promise type niet; ze voegen taalzoekwoorden toe die beloftes gemakkelijker maken om te bellen. Ze zijn uitwisselbaar:

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) { ... }

Elke functie die ketens van beloften gebruikt, kan worden herschreven met 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));
 }

De functie kan als volgt worden herschreven met async / await :

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);
  }
}

Deze async variant van newUnicorn() lijkt een terugkeer Promise , maar eigenlijk waren er meerdere await zoekwoorden. Elke beloofde een Promise , dus eigenlijk hadden we een verzameling beloften in plaats van een ketting.

In feite kunnen we het beschouwen als een function* -generator, met elk await op een yield new Promise . De resultaten van elke belofte zijn echter nodig door de volgende om de functie voort te zetten. Dit is de reden waarom het extra sleutelwoord async nodig is voor de functie (evenals het sleutelwoord await bij het aanroepen van de beloften) omdat het Javascript vertelt om automatisch een waarnemer voor deze iteratie te creëren. De Promise geretourneerd door de async function newUnicorn() wanneer deze iteratie is voltooid.

In de praktijk hoeft u dat niet in overweging te nemen; await verbergt de belofte en async verbergt de generator iteratie.

U kunt async functies aanroepen alsof het beloften zijn en await elke belofte of elke async functie. U hoeft niet te await een async-functie, net zoals u een belofte kunt uitvoeren zonder een .then() .

U kunt ook een async IIFE gebruiken als u die code onmiddellijk wilt uitvoeren:

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

Wacht met async

Wanneer u async in loops gebruikt, kunt u enkele van deze problemen tegenkomen.

Als je gewoon probeert await inside forEach , geeft dit een Unexpected token forEach .

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

Dit komt doordat je de pijl ten onrechte als een blok hebt gezien. Het await is in de context van de callback-functie, die niet async .
De tolk beschermt ons tegen het maken van de bovenstaande fout, maar als u async toevoegt aan de voor forEach callback worden er geen fouten gegenereerd. Je denkt misschien dat dit het probleem oplost, maar het zal niet werken zoals verwacht.

Voorbeeld:

(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');
})();

Dit gebeurt omdat de callback async-functie alleen zichzelf kan pauzeren, niet de ouder-async-functie.

Je zou een asyncForEach-functie kunnen schrijven die een belofte retourneert en dan kun je zoiets await asyncForEach(async (e) => await somePromiseFn(e), data ) In principe retourneer je een belofte die oplost wanneer alle callbacks worden afgewacht en gedaan. Maar er zijn betere manieren om dit te doen, en dat is gewoon een lus gebruiken.

U kunt gebruik maken van een for-of lus of een for/while loop, maakt het eigenlijk niet uit welke je kiest.

(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');
})();

Maar er is nog een vangst. Deze oplossing wacht tot elk gesprek naar somePromiseFn is voltooid voordat het volgende wordt somePromiseFn .
Dit is geweldig als u daadwerkelijk wilt dat uw somePromiseFn aanroepen worden uitgevoerd in volgorde, maar als u wilt dat ze tegelijkertijd worden uitgevoerd, moet u await op 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 ontvangt een reeks beloften als enige parameter en retourneert een belofte. Wanneer alle beloften in de array zijn opgelost, wordt de geretourneerde belofte ook opgelost. We await op die belofte en wanneer deze is opgelost, zijn al onze waarden beschikbaar.

De bovenstaande voorbeelden zijn volledig uitvoerbaar. De somePromiseFn functie kan worden gemaakt als een async echo-functie met een time-out. Je kunt de voorbeelden in de babel-repl uitproberen met tenminste de stage-3 preset en naar de output kijken.

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

Gelijktijdige asynchrone (parallelle) bewerkingen

Vaak wilt u asynchrone bewerkingen parallel uitvoeren. Er is een directe syntaxis die dit ondersteunt in het async / await voorstel, maar omdat await wacht op een belofte, kun je meerdere beloften samen in Promise.all om op ze te wachten:

// 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.
}

Dit zal elke zoekopdracht doen om de berichten van elke vriend in serie te krijgen, maar ze kunnen tegelijkertijd worden gedaan:

// 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.
}

Hiermee wordt de lijst met ID's herhaald om een reeks beloften te maken. await wacht tot alle beloften zijn voltooid. Promise.all combineert ze in één enkele belofte, maar ze worden parallel uitgevoerd.



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow