Ejja nħarsu lejn Async/Await f'JavaScript billi tuża eżempji

L-awtur tal-artiklu jeżamina eżempji ta 'Async/Await f'JavaScript. B'mod ġenerali, Async/Await huwa mod konvenjenti biex tikteb kodiċi asinkronu. Qabel ma dehret din il-karatteristika, kodiċi bħal dan inkiteb bl-użu ta 'callbacks u wegħdiet. L-awtur tal-artiklu oriġinali jiżvela l-vantaġġi ta 'Async/Await billi janalizza diversi eżempji.

Infakkrukom: għall-qarrejja kollha ta '"Habr" - skont ta' 10 rublu meta tirreġistra fi kwalunkwe kors ta 'Skillbox billi tuża l-kodiċi promozzjonali "Habr".

Skillbox jirrakkomanda: Kors edukattiv onlajn "Żviluppatur Java".

Callback

Is-sejħa lura hija funzjoni li s-sejħa tagħha tittardja b'mod indefinit. Preċedentement, callbacks kienu użati f'dawk l-oqsma tal-kodiċi fejn ir-riżultat ma setax jinkiseb immedjatament.

Hawn hu eżempju ta 'qari asinkroniku ta' fajl f'Node.js:

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

Il-problemi jinqalgħu meta jkollok bżonn twettaq diversi operazzjonijiet asinkroniċi f'daqqa. Ejja nimmaġinaw dan ix-xenarju: issir talba lid-database tal-utenti Arfat, trid taqra l-qasam profile_img_url tagħha u tniżżel immaġni mis-server someserver.com.
Wara t-tniżżil, aħna nikkonverti l-immaġni għal format ieħor, pereżempju minn PNG għal JPEG. Jekk il-konverżjoni kienet ta' suċċess, tintbagħat ittra lill-email tal-utent. Sussegwentement, l-informazzjoni dwar l-avveniment tiddaħħal fil-fajl transformations.log, li tindika d-data.

Ta 'min joqgħod attent għall-koinċidenza ta' callbacks u n-numru kbir ta '}) fil-parti finali tal-kodiċi. Huwa msejjaħ Callback Hell jew Pyramid of Doom.

L-iżvantaġġi ta 'dan il-metodu huma ovvji:

  • Dan il-kodiċi huwa diffiċli biex jinqara.
  • Huwa wkoll diffiċli li timmaniġġja l-iżbalji, li ħafna drabi jwassal għal kwalità fqira tal-kodiċi.

Biex issolvi din il-problema, wegħdiet ġew miżjuda mal-JavaScript. Huma jippermettu li inti tissostitwixxi nesting fil-fond ta 'callbacks bil-kelma .then.

L-aspett pożittiv tal-wegħdiet huwa li jagħmlu l-kodiċi jinqara ħafna aħjar, minn fuq għal isfel aktar milli mix-xellug għal-lemin. Madankollu, il-wegħdiet għandhom ukoll il-problemi tagħhom:

  • Trid iżżid ħafna .imbagħad.
  • Minflok try/catch, .catch jintuża biex jimmaniġġja l-iżbalji kollha.
  • Ħidma ma 'wegħdiet multipli f'linja waħda mhux dejjem konvenjenti f'xi każijiet, dawn jikkomplikaw il-kodiċi.

Hawnhekk hawn problema li se turi t-tifsira tal-aħħar punt.

Ejja ngħidu li għandna for loop li jistampa sekwenza ta' numri minn 0 sa 10 f'intervalli każwali (0–n sekondi). Bl-użu tal-wegħdiet, għandek bżonn tibdel dan il-linja sabiex in-numri jiġu stampati f'sekwenza minn 0 sa 10. Allura, jekk tieħu 6 sekondi biex tipprintja żero u 2 sekondi biex tipprintja waħda, iż-żero għandu jiġi stampat l-ewwel, u mbagħad jibda l-countdown għall-istampar tal-wieħed.

U ovvjament, ma nużawx Async/Await jew .sort biex insolvu din il-problema. Soluzzjoni ta 'eżempju hija fl-aħħar.

Funzjonijiet Async

Iż-żieda ta 'funzjonijiet async fl-ES2017 (ES8) issimplifikat il-kompitu ta' ħidma bil-wegħdiet. Ninnota li l-funzjonijiet async jaħdmu "fuq" il-wegħdiet. Dawn il-funzjonijiet ma jirrappreżentawx kunċetti kwalitattivament differenti. Il-funzjonijiet Async huma maħsuba bħala alternattiva għall-kodiċi li juża l-wegħdiet.

Async/Await jagħmilha possibbli li jiġi organizzat ix-xogħol b'kodiċi asinkroniku fi stil sinkroniku.

Għalhekk, li tkun taf wegħdiet tagħmilha aktar faċli li wieħed jifhem il-prinċipji ta 'Async/Await.

sintassi

Normalment tikkonsisti f'żewġ kliem prinċipali: async u await. L-ewwel kelma ddawwar il-funzjoni f'asynchronous. Funzjonijiet bħal dawn jippermettu l-użu ta 'wait. Fi kwalunkwe każ ieħor, l-użu ta 'din il-funzjoni se jiġġenera żball.

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

Async jiddaħħal fil-bidu nett tad-dikjarazzjoni tal-funzjoni, u fil-każ ta' funzjoni tal-vleġġa, bejn is-sinjal "=" u l-parentesi.

Dawn il-funzjonijiet jistgħu jitqiegħdu f'oġġett bħala metodi jew jintużaw f'dikjarazzjoni ta' klassi.

// 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! Ta 'min jiftakar li l-kostrutturi tal-klassi u l-getters/setters ma jistgħux ikunu asinkroniċi.

Semantika u regoli ta' eżekuzzjoni

Il-funzjonijiet Async huma bażikament simili għal funzjonijiet standard JS, iżda hemm eċċezzjonijiet.

Għalhekk, il-funzjonijiet async dejjem jirritornaw wegħdiet:

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

Speċifikament, fn jirritorna l-istring hello. Ukoll, peress li din hija funzjoni asinkronika, il-valur tal-istring huwa mgeżwer f'wegħda bl-użu ta 'kostruttur.

Hawn disinn alternattiv mingħajr Async:

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

F'dan il-każ, il-wegħda tiġi rritornata "manwalment". Funzjoni asinkronika hija dejjem imgeżwer f'wegħda ġdida.

Jekk il-valur tar-ritorn huwa primittiv, il-funzjoni asinkronika tirritorna l-valur billi tgeżwih f'wegħda. Jekk il-valur tar-ritorn huwa oġġett tal-wegħda, ir-riżoluzzjoni tiegħu tiġi rritornata f'wegħda ġdida.

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

Imma x'jiġri jekk ikun hemm żball ġewwa funzjoni asinkronika?

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

Jekk ma jiġix ipproċessat, foo() jirritorna wegħda b'ċaħda. F'din is-sitwazzjoni, Promise.reject li jkun fih żball se jintbagħat lura minflok Promise.resolve.

Il-funzjonijiet Async dejjem joħorġu wegħda, irrispettivament minn dak li jiġi rritornat.

Funzjonijiet asinkroniċi jieqaf fuq kull tistenna.

Await taffettwa l-espressjonijiet. Għalhekk, jekk l-espressjoni hija wegħda, il-funzjoni asinkronika tiġi sospiża sakemm titwettaq il-wegħda. Jekk l-espressjoni mhix wegħda, hija kkonvertita għal wegħda permezz ta 'Promise.resolve u mbagħad titlesta.

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

U hawnhekk hija deskrizzjoni ta 'kif taħdem il-funzjoni fn.

  • Wara li ssejjaħlu, l-ewwel linja tiġi kkonvertita minn const a = await 9; in const a = await Promise.resolve(9);.
  • Wara li tuża Await, l-eżekuzzjoni tal-funzjoni hija sospiża sakemm a tikseb il-valur tagħha (fis-sitwazzjoni attwali hija 9).
  • delayAndGetRandom(1000) iwaqqaf l-eżekuzzjoni tal-funzjoni fn sakemm titlesta ruħha (wara sekonda 1). Dan effettivament iwaqqaf il-funzjoni fn għal sekonda.
  • delayAndGetRandom(1000) permezz tar-riżoluzzjoni jirritorna valur każwali, li mbagħad jiġi assenjat lill-varjabbli b.
  • Ukoll, il-każ bil-varjabbli c huwa simili għall-każ bil-varjabbli a. Wara dan, kollox jieqaf għal sekonda, iżda issa delayAndGetRandom(1000) ma jirritorna xejn għax mhux meħtieġ.
  • Bħala riżultat, il-valuri huma kkalkulati bl-użu tal-formula a + b * c. Ir-riżultat huwa mgeżwer f'wegħda bl-użu ta 'Promise.resolve u rritornat mill-funzjoni.

Dawn il-pawżi jistgħu jkunu reminixxenti tal-ġeneraturi fl-ES6, iżda hemm xi ħaġa għaliha ir-raġunijiet tiegħek.

Issolvi l-problema

Ukoll, issa ejja nħarsu lejn is-soluzzjoni għall-problema msemmija hawn fuq.

Il-funzjoni finishMyTask tuża Await biex tistenna r-riżultati ta' operazzjonijiet bħal queryDatabase, sendEmail, logTaskInFile, u oħrajn. Jekk tqabbel din is-soluzzjoni ma’ dik fejn intużaw il-wegħdiet, is-similaritajiet isiru ovvji. Madankollu, il-verżjoni Async/Await tissimplifika bil-kbir il-kumplessitajiet sintattiċi kollha. F'dan il-każ, m'hemm l-ebda numru kbir ta 'callbacks u ktajjen bħal .then/.catch.

Hawnhekk hawn soluzzjoni bl-output ta 'numri, hemm żewġ għażliet.

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

U hawnhekk hija soluzzjoni li tuża funzjonijiet async.

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

Żball fl-ipproċessar

Żbalji mhux immaniġġjati huma mgeżwra f'wegħda miċħuda. Madankollu, il-funzjonijiet async jistgħu jużaw try/catch biex jimmaniġġjaw l-iżbalji b'mod sinkroniku.

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

canRejectOrReturn() hija funzjoni asinkronika li jew tirnexxi (“numru perfett”) jew tfalli bi żball (“Skużani, numru kbir wisq”).

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

Peress li l-eżempju ta 'hawn fuq jistenna li canRejectOrReturn tesegwixxi, in-nuqqas tiegħu stess jirriżulta fl-eżekuzzjoni tal-blokk tal-qbid. Bħala riżultat, il-funzjoni foo se tispiċċa jew mhux definit (meta xejn ma jiġi rritornat fil-blokk try) jew bi żball maqbud. Bħala riżultat, din il-funzjoni mhux se tfalli minħabba li l-prova/qabda se tieħu ħsieb il-funzjoni foo innifsu.

Hawn eżempju ieħor:

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

Ta 'min joqgħod attent għall-fatt li fl-eżempju, canRejectOrReturn jintbagħat lura minn foo. Foo f'dan il-każ jew jispiċċa b'numru perfett jew jirritorna Żball ("Skużani, numru kbir wisq"). Il-blokk tal-qbid qatt mhu se jiġi eżegwit.

Il-problema hija li foo jirritorna l-wegħda mgħoddija minn canRejectOrReturn. Allura s-soluzzjoni għal foo ssir is-soluzzjoni għal canRejectOrReturn. F'dan il-każ, il-kodiċi se jikkonsisti biss f'żewġ linji:

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

Hawn x'jiġri jekk tuża stenna u terġa' lura flimkien:

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

Fil-kodiċi ta 'hawn fuq, foo se joħroġ b'suċċess kemm b'numru perfett kif ukoll bi żball maqbud. Mhux se jkun hemm rifjuti hawn. Iżda foo se jirritorna ma 'canRejectOrReturn, mhux mhux definit. Ejja niżguraw dan billi tneħħi l-linja tar-ritorn await canRejectOrReturn():

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

Żbalji u nases komuni

F'xi każijiet, l-użu ta 'Async/Await jista' jwassal għal żbalji.

Tistenna minsija

Dan jiġri spiss - il-kelma prinċipali tistenna tinsa qabel il-wegħda:

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

Kif tistgħu taraw, m'hemm l-ebda stennija jew ritorn fil-kodiċi. Għalhekk foo dejjem joħroġ b'undefined mingħajr dewmien ta' sekonda. Imma l-wegħda se titwettaq. Jekk tarmi żball jew rifjut, allura UnhandledPromiseRejectionWarning se tissejjaħ.

Funzjonijiet Async f'Callbacks

Il-funzjonijiet Async huma spiss użati f'.map jew .filter bħala callbacks. Eżempju huwa l-funzjoni fetchPublicReposCount(username), li tirritorna n-numru ta’ repożitorji miftuħa fuq GitHub. Ejja ngħidu li hemm tliet utenti li l-metriċi tagħhom għandna bżonn. Hawn hu l-kodiċi għal dan il-kompitu:

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'];
}

Għandna bżonn ArfatSalman, octocat, norvig accounts. F'dan il-każ nagħmlu:

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

Ta 'min joqgħod attent għal Await fil-callback .map. Hawnhekk jgħodd hija firxa ta 'wegħdiet, u .map huwa callback anonimu għal kull utent speċifikat.

Użu konsistenti żżejjed ta 'wait

Ejja nieħdu dan il-kodiċi bħala eżempju:

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

Hawnhekk in-numru repo jitqiegħed fil-varjabbli tal-għadd, imbagħad dan in-numru jiżdied mal-firxa tal-għadd. Il-problema bil-kodiċi hija li sakemm tasal id-dejta tal-ewwel utent mis-server, l-utenti sussegwenti kollha jkunu fil-modalità standby. Għalhekk, utent wieħed biss jiġi pproċessat kull darba.

Jekk, pereżempju, tieħu madwar 300 ms biex tipproċessa utent wieħed, allura għall-utenti kollha huwa diġà t-tieni il-ħin mgħoddi b'mod lineari jiddependi min-numru ta 'utenti. Iżda peress li l-kisba tan-numru ta 'repo ma tiddependix minn xulxin, il-proċessi jistgħu jiġu parallelizzati. Dan jeħtieġ li taħdem ma .map u Promise.all:

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

Promise.all jirċievi firxa ta' wegħdiet bħala input u jirritorna wegħda. Dan tal-aħħar, wara li l-wegħdiet kollha fil-firxa jkunu tlestew jew fl-ewwel rifjut, jitlesta. Jista 'jiġri li kollha ma jibdewx fl-istess ħin - sabiex jiġi żgurat bidu simultanju, tista' tuża p-map.

Konklużjoni

Il-funzjonijiet async qed isiru dejjem aktar importanti għall-iżvilupp. Ukoll, għall-użu adattiv ta 'funzjonijiet async ta' min jużah Iteraturi Async. Żviluppatur JavaScript għandu jkun kapaċi sew f'dan.

Skillbox jirrakkomanda:

Sors: www.habr.com

Żid kumment