Sök…


Introduktion

async och await bygga på toppen av löften och generatorer för att uttrycka asynkrona handlingar inline. Detta gör asynkron eller återuppringningskod mycket lättare att underhålla.

Funktioner med sökordet async returnerar ett Promise och kan kallas med den syntaxen.

Inuti en async function kan nyckelordet await tillämpas på valfritt Promise och kommer att leda till att hela funktionsorganet efter att await körs efter att löften löses.

Syntax

  • async-funktion foo () {
    ...
    vänta på asyncCall ()
    }
  • async-funktion () {...}
  • async () => {...}
  • (async () => {
    const data = vänta på asyncCall ()
    console.log (data)}) ()

Anmärkningar

Async-funktioner är ett syntaktiskt socker framför löften och generatorer. De hjälper dig att göra din kod mer läsbar, underhållbar, lättare att fånga fel i och med färre indragningsnivåer.

Introduktion

En funktion som definieras som async är en funktion som kan utföra asynkrona åtgärder men fortfarande ser synkron ut. Hur det görs är att använda nyckelordet för att await att skjuta upp funktionen medan den väntar på att ett löfte löser eller avvisar.

Obs: Async-funktioner är ett steg 4 ("Färdigt") förslag på spår som ska inkluderas i ECMAScript 2017-standarden.

Använd t.ex. det löftebaserade 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);
    }
}

En async-funktion returnerar alltid ett löfte själv, så du kan använda den i andra asynkrona funktioner.

Pilfunktionsstil

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

Mindre intryck

Med löften:

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

Med async-funktioner:

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

Lägg märke till hur returen är längst ner och inte längst upp, och du använder språkets ursprungliga felhanteringsmekanik ( try/catch ).

Vänta och operatörens företräde

Du måste ha operatörens prioritet i åtanke när du använder nyckelordet await .

Föreställ dig att vi har en asynkron funktion som kallar en annan asynkron funktion, getUnicorn() som returnerar ett löfte som löser till en instans av klass Unicorn . Nu vill vi få storleken på enhörningen med getSize() -metoden för den klassen.

Titta på följande kod:

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

Vid första anblicken verkar det giltigt, men det är det inte. På grund av operatörens förekomst motsvarar det följande:

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

Här försöker vi kalla getSize() för Promise-objektet, vilket inte är vad vi vill ha.

Istället bör vi använda parenteser för att ange att vi först vill vänta på enhörningen och sedan ringa getSize() -metoden för resultatet:

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

Självklart. den föregående versionen kan vara giltig i vissa fall, till exempel om getUnicorn() -funktionen var synkron, men getSize() -metoden var asynkron.

Async-funktioner jämfört med löften

async funktioner ersätter inte Promise typen; de lägger till språkord som gör löften enklare att ringa. De är utbytbara:

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

Alla funktioner som använder löftskedjor kan skrivas om med 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));
 }

Funktionen kan skrivas om med hjälp av async / await enligt följande:

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

Denna async variant av newUnicorn() verkar ge ett Promise , men det fanns verkligen flera await nyckelord. Var och en gav tillbaka ett Promise , så vi hade verkligen en samling löften snarare än en kedja.

I själva verket kan vi tänka på det som en function* , var och en await vara ett yield new Promise . Men resultaten av varje löfte behövs av nästa för att fortsätta funktionen. Därför behövs det ytterligare nyckelordet async för funktionen (liksom det await nyckelordet när man ringer löften) eftersom det berättar Javascript att automatiskt skapar en observatör för den här iterationen. Promise returneras av async function newUnicorn() löses när denna iteration är klar.

I praktiken behöver du inte ta hänsyn till det; await döljer löfte och async döljer generatorgenereringen.

Du kan ringa async funktioner som om de var löften och await alla löften eller någon async funktion. Du behöver inte await en async-funktion, precis som du kan utföra ett löfte utan .then() .

Du kan också använda en async IIFE om du vill köra den koden omedelbart:

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

Looping med async väntar

När du använder async väntar i öglor kan du stöta på några av dessa problem.

Om du bara försöker använda vänta inuti för forEach kommer det att kasta ett Unexpected token .

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

Detta kommer av det faktum att du felaktigt har sett pilens funktion som ett block. await kommer att ske i samband med återuppringningsfunktionen, som inte är async .
Tolkaren skyddar oss från att göra ovanstående fel, men om du lägger till async i forEach återuppringningen forEach inga fel. Du kanske tror att detta löser problemet, men det fungerar inte som förväntat.

Exempel:

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

Detta händer eftersom funktionen för återuppringning av asynk bara kan pausa sig själv, inte förälderens asynkfunktion.

Du kan skriva en asyncForEach-funktion som returnerar ett löfte och sedan kan du något liknande await asyncForEach(async (e) => await somePromiseFn(e), data ) I grund och botten returnerar du ett löfte som löses när alla återuppringningar väntar och görs. Men det finns bättre sätt att göra detta, och det är bara att använda en slinga.

Du kan använda en for-of loop eller en for/while slinga, det spelar ingen roll vilken du väljer.

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

Men det finns en annan fångst. Denna lösning väntar på att varje samtal till somePromiseFn ska slutföras innan den itereras över nästa.
Det här är bra om du faktiskt vill att dina somePromiseFn kallelser ska utföras i ordning, men om du vill att de ska köras samtidigt måste du awaitPromise.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 får en rad löften som sin enda parameter och ger ett löfte. När alla löften i arrayen löses, löses också det återlämnade löfte. Vi await på detta löfte och när det löses är alla våra värden tillgängliga.

Ovanstående exempel är helt körbara. Funktionen somePromiseFn kan göras som en async-ekofunktion med en timeout. Du kan prova exemplen i babel-repl med åtminstone stage-3 förinställningen och titta på utgången.

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

Samtidig asynk (parallell) operation

Ofta vill du utföra asynkrona operationer parallellt. Det finns direkt syntax som stöder detta i async / await förslag, men eftersom await kommer att vänta på ett löfte, kan du linda flera löften tillsammans i Promise.all vänta för dem:

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

Detta gör varje fråga för att få varje väns inlägg seriellt, men de kan göras samtidigt:

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

Detta kommer att slinga över listan över ID: er för att skapa en rad löften. await väntar på att alla löften är fullständiga. Promise.all kombinerar dem till ett enda löfte, men de görs parallellt.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow