Atong tan-awon ang Async/Await sa JavaScript gamit ang mga pananglitan

Ang tagsulat sa artikulo nagsusi sa mga pananglitan sa Async/Await sa JavaScript. Sa kinatibuk-an, ang Async/Await usa ka sayon ​​​​nga paagi sa pagsulat sa asynchronous code. Sa wala pa kini nga bahin nagpakita, ang ingon nga code gisulat gamit ang mga callback ug mga saad. Ang tagsulat sa orihinal nga artikulo nagpadayag sa mga bentaha sa Async / Paghulat pinaagi sa pag-analisar sa lainlaing mga pananglitan.

Gipahinumduman namon ikaw: alang sa tanan nga mga magbabasa sa "Habr" - usa ka diskwento sa 10 nga mga rubles kung nagpalista sa bisan unsang kurso sa Skillbox gamit ang code sa promosyon nga "Habr".

Girekomenda sa Skillbox: Online nga kurso sa edukasyon "Java Developer".

callback

Ang callback usa ka function kansang tawag nalangan hangtod sa hangtod. Kaniadto, ang mga callback gigamit sa mga lugar sa code diin ang resulta dili dayon makuha.

Ania ang usa ka pananglitan sa asynchronous nga pagbasa sa usa ka file sa Node.js:

fs.readFile(__filename, 'utf-8', (err, data) => {
  if (err) {
    throw err;
  }
  console.log(data);
});

Ang mga problema motungha kung kinahanglan nimo nga himuon ang daghang mga asynchronous nga operasyon sa usa ka higayon. Hunahunaa kini nga senaryo: usa ka hangyo ang gihimo sa database sa gumagamit sa Arfat, kinahanglan nimo nga basahon ang profile_img_url nga field ug mag-download ug imahe gikan sa someserver.com server.
Pagkahuman sa pag-download, among gi-convert ang imahe sa lain nga format, pananglitan gikan sa PNG hangtod sa JPEG. Kung malampuson ang pagkakabig, usa ka sulat ang ipadala sa email sa tiggamit. Sunod, ang impormasyon bahin sa panghitabo gisulod sa transformations.log file, nga nagpaila sa petsa.

Angayan nga hatagan ug pagtagad ang overlap sa mga callback ug ang dako nga gidaghanon sa }) sa katapusang bahin sa code. Gitawag kini nga Callback Hell o Pyramid of Doom.

Ang mga disbentaha niini nga pamaagi klaro:

  • Lisod basahon kini nga code.
  • Lisud usab ang pagdumala sa mga kasaypanan, nga kasagaran mosangpot sa dili maayo nga kalidad sa code.

Aron masulbad kini nga problema, ang mga saad gidugang sa JavaScript. Gitugotan ka nila nga ilisan ang lawom nga pagpugad sa mga callback sa pulong .dayon.

Ang positibo nga aspeto sa mga saad mao nga ilang gihimo ang code nga labi ka dali nga mabasa, gikan sa taas hangtod sa ubos kaysa gikan sa wala hangtod sa tuo. Bisan pa, ang mga saad adunay mga problema usab:

  • Kinahanglan nimong idugang ang daghang .unya.
  • Inay sa pagsulay/pagdakop, .catch gigamit sa pagdumala sa tanang mga sayop.
  • Ang pagtrabaho uban ang daghang mga saad sulod sa usa ka loop dili kanunay nga kombenyente; sa pipila ka mga kaso, gikomplikado nila ang code.

Ania ang problema nga magpakita sa kahulogan sa kataposang punto.

Ibutang ta nga kita adunay usa ka for loop nga nag-imprinta sa usa ka han-ay sa mga numero gikan sa 0 ngadto sa 10 sa random intervals (0-n segundos). Gamit ang mga saad, kinahanglan nimo nga usbon kini nga loop aron ang mga numero maimprinta sa han-ay gikan sa 0 ngadto sa 10. Busa, kung gikinahanglan ang 6 segundos sa pag-imprinta sa usa ka zero ug 2 segundos sa pag-imprinta sa usa, ang zero kinahanglan nga i-imprinta una, ug dayon ang pag-ihap sa pag-imprenta sa usa magsugod.

Ug siyempre, wala kami mogamit sa Async/Await o .sort aron masulbad kini nga problema. Ang usa ka pananglitan nga solusyon anaa sa katapusan.

Async nga mga gimbuhaton

Ang pagdugang sa mga function sa async sa ES2017 (ES8) gipasimple ang tahas sa pagtrabaho kauban ang mga saad. Namatikdan nako nga ang mga function sa async nagtrabaho "sa ibabaw" sa mga saad. Kini nga mga gimbuhaton wala magrepresentar sa lainlain nga mga konsepto sa kwalitatibo. Ang mga function sa Async gituyo isip alternatibo sa code nga naggamit sa mga saad.

Ang Async/Await nagpaposible sa pag-organisar sa trabaho gamit ang asynchronous code sa usa ka dungan nga estilo.

Busa, ang pagkahibalo sa mga saad makapasayon ​​sa pagsabot sa mga prinsipyo sa Async/Await.

syntax

Kasagaran kini naglangkob sa duha ka mga keyword: async ug naghulat. Ang unang pulong naghimo sa function ngadto sa asynchronous. Ang ingon nga mga gimbuhaton nagtugot sa paggamit sa paghulat. Sa bisan unsa nga kaso, ang paggamit niini nga function makamugna og usa ka sayup.

// With function declaration
 
async function myFn() {
  // await ...
}
 
// With arrow function
 
const myFn = async () => {
  // await ...
}
 
function myFn() {
  // await fn(); (Syntax Error since no async)
}
 

Ang Async gisal-ut sa sinugdanan sa deklarasyon sa function, ug sa kaso sa usa ka arrow function, tali sa "=" sign ug sa mga parentesis.

Kini nga mga gimbuhaton mahimong ibutang sa usa ka butang ingon nga mga pamaagi o gigamit sa usa ka deklarasyon sa klase.

// As an object's method
 
const obj = {
  async getName() {
    return fetch('https://www.example.com');
  }
}
 
// In a class
 
class Obj {
  async getResource() {
    return fetch('https://www.example.com');
  }
}

NB! Angay nga hinumdoman nga ang mga tigtukod sa klase ug mga getter/setters dili mahimong asynchronous.

Semantiko ug mga lagda sa pagpatuman

Ang mga pag-andar sa Async sa panguna parehas sa naandan nga mga gimbuhaton sa JS, apan adunay mga eksepsiyon.

Sa ingon, ang mga function sa async kanunay nga nagbalik sa mga saad:

async function fn() {
  return 'hello';
}
fn().then(console.log)
// hello

Sa espesipiko, gibalik sa fn ang string hello. Aw, tungod kay kini usa ka asynchronous function, ang string value giputos sa usa ka saad gamit ang usa ka constructor.

Ania ang usa ka alternatibo nga disenyo nga walay Async:

function fn() {
  return Promise.resolve('hello');
}
 
fn().then(console.log);
// hello

Sa kini nga kaso, ang saad gibalik nga "manual". Ang usa ka asynchronous nga function kanunay giputos sa usa ka bag-ong saad.

Kung ang pagbalik nga kantidad usa ka primitive, ang async function nagbalik sa kantidad pinaagi sa pagputos niini sa usa ka saad. Kung ang kantidad sa pagbalik usa ka butang nga saad, ang resolusyon niini ibalik sa usa ka bag-ong saad.

const p = Promise.resolve('hello')
p instanceof Promise;
// true
 
Promise.resolve(p) === p;
// true
 

Apan unsa ang mahitabo kung adunay usa ka sayup sa sulod sa usa ka asynchronous function?

async function foo() {
  throw Error('bar');
}
 
foo().catch(console.log);

Kung dili kini maproseso, ang foo() magbalik sa usa ka saad nga adunay pagsalikway. Niini nga sitwasyon, ang Promise.reject nga adunay kasaypanan ibalik imbes nga Promise.resolve.

Ang mga function sa Async kanunay nga nagpagawas sa usa ka saad, bisan unsa pa ang gibalik.

Ang mga asynchronous function mohunong sa matag paghulat.

Ang paghulat makaapekto sa mga ekspresyon. Busa, kung ang ekspresyon usa ka saad, ang async function gisuspinde hangtod ang saad matuman. Kung ang ekspresyon dili usa ka saad, kini makabig sa usa ka saad pinaagi sa Promise.resolve ug dayon makompleto.

// utility function to cause delay
// and get random value
 
const delayAndGetRandom = (ms) => {
  return new Promise(resolve => setTimeout(
    () => {
      const val = Math.trunc(Math.random() * 100);
      resolve(val);
    }, ms
  ));
};
 
async function fn() {
  const a = await 9;
  const b = await delayAndGetRandom(1000);
  const c = await 5;
  await delayAndGetRandom(1000);
 
  return a + b * c;
}
 
// Execute fn
fn().then(console.log);

Ug ania ang usa ka paghulagway kung giunsa ang paglihok sa fn.

  • Human sa pagtawag niini, ang unang linya nakabig gikan sa const a = maghulat 9; sa const a = maghulat sa Promise.resolve(9);.
  • Human sa paggamit sa Paghulat, ang pagpatuman sa function gisuspinde hangtud nga makuha ang bili niini (sa kasamtangan nga sitwasyon kini 9).
  • delayAndGetRandom(1000) mohunong sa pagpatuman sa fn function hangtud nga kini makompleto sa iyang kaugalingon (human sa 1 segundos). Kini epektibo nga mohunong sa fn function sulod sa 1 segundos.
  • delayAndGetRandom(1000) pinaagi sa pagsulbad mibalik sa usa ka random nga bili, nga unya gi-assign sa variable b.
  • Aw, ang kaso nga adunay variable c parehas sa kaso nga adunay variable a. Pagkahuman niana, ang tanan mihunong sa usa ka segundo, apan karon ang delayAndGetRandom(1000) wala’y gibalik tungod kay wala kini kinahanglan.
  • Ingon usa ka sangputanan, ang mga kantidad gikalkula gamit ang pormula a + b * c. Ang resulta giputos sa usa ka saad gamit ang Promise.resolve ug gibalik sa function.

Kini nga mga paghunong mahimong makapahinumdom sa mga generator sa ES6, apan adunay usa ka butang niini imong mga rason.

Pagsulbad sa problema

Aw, karon atong tan-awon ang solusyon sa problema nga gihisgotan sa ibabaw.

Ang finishMyTask function naggamit sa Paghulat sa paghulat sa mga resulta sa mga operasyon sama sa queryDatabase, sendEmail, logTaskInFile, ug uban pa. Kung imong itandi kini nga solusyon sa usa diin gigamit ang mga saad, ang pagkaparehas mahimong klaro. Bisan pa, ang bersyon sa Async/Await labi nga nagpasimple sa tanan nga mga komplikado nga syntactic. Niini nga kaso, walay daghang mga callback ug mga kadena sama sa .then/.catch.

Ania ang usa ka solusyon sa output sa mga numero, adunay duha ka mga kapilian.

const wait = (i, ms) => new Promise(resolve => setTimeout(() => resolve(i), ms));
 
// Implementation One (Using for-loop)
const printNumbers = () => new Promise((resolve) => {
  let pr = Promise.resolve(0);
  for (let i = 1; i <= 10; i += 1) {
    pr = pr.then((val) => {
      console.log(val);
      return wait(i, Math.random() * 1000);
    });
  }
  resolve(pr);
});
 
// Implementation Two (Using Recursion)
 
const printNumbersRecursive = () => {
  return Promise.resolve(0).then(function processNextPromise(i) {
 
    if (i === 10) {
      return undefined;
    }
 
    return wait(i, Math.random() * 1000).then((val) => {
      console.log(val);
      return processNextPromise(i + 1);
    });
  });
};

Ug ania ang usa ka solusyon gamit ang mga function sa async.

async function printNumbersUsingAsync() {
  for (let i = 0; i < 10; i++) {
    await wait(i, Math.random() * 1000);
    console.log(i);
  }
}

Sayop sa pagproseso

Ang mga sayop nga wala maatiman giputos sa usa ka gisalikway nga saad. Bisan pa, ang mga function sa async mahimong mogamit pagsulay / pagdakop aron madumala ang mga sayup nga dungan.

async function canRejectOrReturn() {
  // wait one second
  await new Promise(res => setTimeout(res, 1000));
 
// Reject with ~50% probability
  if (Math.random() > 0.5) {
    throw new Error('Sorry, number too big.')
  }
 
return 'perfect number';
}

Ang canRejectOrReturn() usa ka asynchronous function nga molampos (“perpektong numero”) o mapakyas sa usa ka sayop (“Pasayloa, dako kaayo ang numero”).

async function foo() {
  try {
    await canRejectOrReturn();
  } catch (e) {
    return 'error caught';
  }
}

Tungod kay ang panig-ingnan sa ibabaw nagpaabut sa canRejectOrReturn nga ipatuman, ang kaugalingon nga kapakyasan moresulta sa pagpatuman sa catch block. Ingon usa ka sangputanan, ang function foo matapos nga dili matino (kung wala’y ibalik sa try block) o adunay sayup nga nakuha. Ingon usa ka sangputanan, kini nga function dili mapakyas tungod kay ang pagsulay / pagdakop magdumala sa function foo mismo.

Ania ang laing pananglitan:

async function foo() {
  try {
    return canRejectOrReturn();
  } catch (e) {
    return 'error caught';
  }
}

Angayan nga hatagan pagtagad ang kamatuoran nga sa pananglitan, ang canRejectOrReturn gibalik gikan sa foo. Ang Foo sa kini nga kaso mahimo’g matapos sa usa ka hingpit nga numero o ibalik ang usa ka Sayop ("Pasensya, dako kaayo ang numero"). Ang catch block dili gayud ipatuman.

Ang problema mao nga gibalik ni foo ang saad nga gipasa gikan sa canRejectOrReturn. Busa ang solusyon sa foo nahimong solusyon sa canRejectOrReturn. Sa kini nga kaso, ang code maglangkob lamang sa duha ka linya:

try {
    const promise = canRejectOrReturn();
    return promise;
}

Ania kung unsa ang mahitabo kung mogamit ka maghulat ug magbalik nga magkauban:

async function foo() {
  try {
    return await canRejectOrReturn();
  } catch (e) {
    return 'error caught';
  }
}

Sa code sa ibabaw, ang foo mogawas nga malampuson nga adunay usa ka hingpit nga numero ug usa ka sayup nga nakuha. Walay mga pagdumili dinhi. Apan ang foo mobalik uban ang canRejectOrReturn, dili sa wala mahibal-an. Atong siguroon kini pinaagi sa pagtangtang sa pagbalik naghulat canRejectOrReturn() linya:

try {
    const value = await canRejectOrReturn();
    return value;
}
// …

Kasagarang mga sayop ug mga lit-ag

Sa pipila ka mga kaso, ang paggamit sa Async/Await mahimong mosangpot sa mga sayup.

Nakalimtan naghulat

Kanunay kini nga mahitabo - ang naghulat nga keyword nakalimtan sa wala pa ang saad:

async function foo() {
  try {
    canRejectOrReturn();
  } catch (e) {
    return 'caught';
  }
}

Sama sa imong nakita, wala’y paghulat o pagbalik sa code. Busa ang foo kanunay nga mogawas nga wala matino nga wala’y usa ka segundo nga paglangan. Apan ang saad matuman. Kung kini adunay sayup o pagsalikway, unya ang UnhandledPromiseRejectionWarning tawgon.

Async Function sa Callbacks

Ang mga function sa Async kasagarang gigamit sa .map o .filter isip mga callback. Usa ka pananglitan mao ang fetchPublicReposCount(username) function, nga nagbalik sa gidaghanon sa bukas nga mga repositoryo sa GitHub. Ingnon ta nga adunay tulo ka mga tiggamit kansang mga sukatan kinahanglan namon. Ania ang code alang niini nga buluhaton:

const url = 'https://api.github.com/users';
 
// Utility fn to fetch repo counts
const fetchPublicReposCount = async (username) => {
  const response = await fetch(`${url}/${username}`);
  const json = await response.json();
  return json['public_repos'];
}

Kinahanglan namon ang ArfatSalman, octocat, norvig nga mga account. Niini nga kaso atong buhaton:

const users = [
  'ArfatSalman',
  'octocat',
  'norvig'
];
 
const counts = users.map(async username => {
  const count = await fetchPublicReposCount(username);
  return count;
});

Angay nga hatagan ug pagtagad ang Paghulat sa .map callback. Dinhi maihap ang usa ka laray sa mga saad, ug ang .map usa ka wala mailhi nga callback alang sa matag piho nga tiggamit.

Sobra nga makanunayon nga paggamit sa paghulat

Atong kuhaon kini nga code isip usa ka pananglitan:

async function fetchAllCounts(users) {
  const counts = [];
  for (let i = 0; i < users.length; i++) {
    const username = users[i];
    const count = await fetchPublicReposCount(username);
    counts.push(count);
  }
  return counts;
}

Dinhi ang repo number gibutang sa count variable, unya kini nga numero idugang sa counts array. Ang problema sa code mao nga hangtod ang data sa una nga tiggamit moabut gikan sa server, ang tanan nga nagsunod nga mga tiggamit naa sa standby mode. Busa, usa ra ka user ang giproseso sa usa ka higayon.

Kung, pananglitan, nagkinahanglag 300 ms aron maproseso ang usa ka tiggamit, nan alang sa tanan nga tiggamit kini usa na ka segundo; ang oras nga gigugol sa linya nagdepende sa gidaghanon sa mga tiggamit. Apan tungod kay ang pagkuha sa gidaghanon sa repo wala magdepende sa usag usa, ang mga proseso mahimong magkaparehas. Kini nagkinahanglan sa pagtrabaho uban sa .map ug Promise.all:

async function fetchAllCounts(users) {
  const promises = users.map(async username => {
    const count = await fetchPublicReposCount(username);
    return count;
  });
  return Promise.all(promises);
}

Ang Promise.all nakadawat og daghang mga saad isip input ug nagbalik sa usa ka saad. Ang naulahi, pagkahuman sa tanan nga mga saad sa laray nga nahuman o sa una nga pagsalikway, nahuman. Mahimong mahitabo nga silang tanan dili magsugod sa samang higayon - aron masiguro ang dungan nga pagsugod, mahimo nimong gamiton ang p-map.

konklusyon

Ang mga gimbuhaton sa Async nahimong labi ka hinungdanon alang sa pag-uswag. Aw, alang sa mapahiangay nga paggamit sa mga function sa async, kinahanglan nimo gamiton Mga Async Iterator. Ang usa ka JavaScript developer kinahanglan nga batid kaayo niini.

Girekomenda sa Skillbox:

Source: www.habr.com

Idugang sa usa ka comment