Breathnaímid ar Async/Await i JavaScript ag úsáid samplaí

Scrúdaíonn údar an ailt samplaí de Async/ag fanacht i JavaScript. Tríd is tríd, is bealach áisiúil é Async/Await chun cód neamhghnách a scríobh. Sula raibh an ghné seo le feiceáil, scríobhadh an cód sin ag baint úsáide as glaonna agus geallúintí. Nochtann údar an ailt bhunaidh na buntáistí a bhaineann le Async/Await trí anailís a dhéanamh ar shamplaí éagsúla.

Meabhraímid: do léitheoirí uile "Habr" - lascaine de 10 rúbal nuair a chláraíonn siad in aon chúrsa Scilbox ag baint úsáide as an gcód bolscaireachta "Habr".

Molann Skillbox: Cúrsa oideachais ar líne "Forbróir Java".

Thug an

Is feidhm é an aisghlaoch a bhfuil a ghlaoch moill ar feadh tréimhse éiginnte. Roimhe seo, baineadh úsáid as glaonna siar sna réimsí cód sin nach bhféadfaí an toradh a fháil láithreach.

Seo sampla de chomhad a léamh go neamhghnách i node.js:

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

Tagann fadhbanna chun cinn nuair is gá duit roinnt oibríochtaí asincrónacha a dhéanamh ag an am céanna. Déanaimis an cás seo a shamhlú: déantar iarratas ar bhunachar sonraí úsáideoirí Arfat, ní mór duit a réimse profile_img_url a léamh agus íomhá a íoslódáil ón bhfreastalaí someserver.com.
Tar éis é a íoslódáil, déanaimid an íomhá a thiontú go formáid eile, mar shampla ó PNG go JPEG. Má d'éirigh leis an tiontú, seoltar litir chuig ríomhphost an úsáideora. Ansin, cuirtear faisnéis faoin imeacht isteach sa chomhad Transforms.log, ag léiriú an dáta.

Is fiú aird a thabhairt ar fhorluí na nglaonna ar ais agus an líon mór }) sa chuid dheireanach den chód. Tugtar Callback Ifreann nó Pirimid na Seirbigh air.

Is léir na míbhuntáistí a bhaineann leis an modh seo:

  • Is deacair an cód seo a léamh.
  • Tá sé deacair freisin earráidí a láimhseáil, rud a fhágann go mbíonn droch-chaighdeán cód go minic.

Chun an fhadhb seo a réiteach, cuireadh gealltanais le JavaScript. Cuireann siad ar do chumas an focal a chur in ionad neadú domhain na n -aisíocaíocht.

Is í an ghné dhearfach de na gealltanais ná go ndéanann siad an cód inléite i bhfad níos fearr, ó bharr go barr seachas ó chlé go deas. Mar sin féin, geallúintí freisin a gcuid fadhbanna:

  • Ní mór duit a lán de .then.
  • In ionad iarracht/ghabháil a dhéanamh, úsáidtear .catch chun gach earráid a láimhseáil.
  • Ní bhíonn obair le hiliomad gealltanais laistigh de lúb amháin áisiúil i gcónaí; i gcásanna áirithe, déanann siad an cód a casta.

Seo fadhb a thaispeánfaidh brí an phointe deiridh.

Cuir in iúl go bhfuil lúb againn a phriontálann seicheamh uimhreacha ó 0 go 10 ag eatraimh randamacha (0 - N soicind). Ag baint úsáide as gealltanais, ní mór duit an lúb seo a athrú ionas go mbeidh na huimhreacha clóite i seicheamh ó 0 go 10. tosóidh an comhaireamh síos le haghaidh priontála.

Agus ar ndóigh, ní úsáidimid Async/Await nó .sort chun an fhadhb seo a réiteach. Tá réiteach samplach ag an deireadh.

Feidhmeanna async

Mar gheall ar fheidhmeanna sioncronaithe a chur leis in ES2017 (ES8) simplíodh an tasc oibriú de réir gealltanais. Tugaim faoi deara go n-oibríonn feidhmeanna async “ar bharr” na ngeallúintí. Ní hionann na feidhmeanna seo agus coincheapa cáilíochtúla éagsúla. Tá feidhmeanna Async beartaithe mar mhalairt ar chód a úsáideann gealltanais.

Mar gheall ar Async/Await, is féidir obair a eagrú le cód neamhghnách i stíl shioncronach.

Mar sin, má bhíonn geallúintí ar eolas agat is fusa prionsabail Async/Await a thuiscint.

error

De ghnáth bíonn dhá eochairfhocal ann: async agus fanacht. Déanann an chéad fhocal an fheidhm a thiontú ina asincrónach. Ceadaíonn feidhmeanna den sórt sin úsáid a bhaint as fanacht. In aon chás eile, ginfidh úsáid na feidhme seo earráid.

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

Cuirtear Async isteach ag tús an dearbhaithe feidhme, agus i gcás feidhm saighead, idir an comhartha “=” agus na lúibíní.

Is féidir na feidhmeanna seo a chur i réad mar mhodhanna nó a úsáid i ndearbhú ranga.

// 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! Is fiú cuimhneamh nach féidir le tógálaithe ranga agus le daoine/le lucht tacaíochta a bheith neamhghnách.

Séimeantaic agus rialacha forghníomhaithe

Tá feidhmeanna async cosúil go bunúsach le feidhmeanna caighdeánacha JS, ach tá eisceachtaí ann.

Mar sin, cuireann feidhmeanna async gealltanais ar ais i gcónaí:

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

Go sonrach, filleann fn an teaghrán dia duit. Bhuel, ós rud é gur feidhm asincrónach é seo, tá an luach teaghrán fillte i ngeallúint ag baint úsáide as cruthaitheoir.

Seo dearadh eile gan Async:

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

Sa chás seo, cuirtear an gealltanas ar ais “de láimh”. Tá feidhm asincrónach fillte i gealltanas nua i gcónaí.

Más luach primitive é an luach tuairisceáin, filleann an fheidhm asincréite an luach trína fhilleadh i ngealltanas. Más rud gealltanas é an luach toraidh, cuirtear a réiteach ar ais i ngealltanas nua.

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

Ach cad a tharlaíonn má tá earráid taobh istigh d'fheidhm asincrónach?

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

Mura ndéantar é a phróiseáil, tabharfaidh foo() gealltanas ar ais le diúltú. Sa chás seo, seolfar Promise.reject ina bhfuil earráid ar ais in ionad Promise.resolve.

Aschuir feidhmeanna Async gealltanas i gcónaí, is cuma cad a chuirtear ar ais.

Feidhmeanna asincrónacha sos ar gach feithimh.

Fanacht tionchar habairtí. Mar sin, más gealltanas é an abairt, cuirtear an fheidhm async ar fionraí go dtí go gcomhlíontar an gealltanas. Mura gealltanas é an abairt, déantar é a thiontú go gealltanas trí ghealltanas.

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

Agus seo cur síos ar conas a oibríonn an fheidhm fn.

  • Tar éis é a ghlaoch, déantar an chéad líne a thiontú ó Const A = ag fanacht 9; ar bhonn leanúnach = ag fanacht le Promise.resolve(9);.
  • Tar éis dó a bheith ag fanacht, cuirtear an fhorghníomhú feidhme ar fionraí go dtí go bhfaigheann A luach (sa chás reatha is é 9) é.
  • Cuireann DelayAndgetRandom (1000) an fheidhm FN i bhfeidhm go dtí go gcríochnaíonn sé é féin (tar éis 1 soicind). Stopann sé seo go héifeachtach an fheidhm FN ar feadh 1 soicind.
  • DelayAndgetRandom (1000) Trí Réitigh Tuairisceáin ar luach randamach, a shanntar ansin don athróg b.
  • Bhuel, tá an cás le hathróg C cosúil leis an gcás le hathróg a. Ina dhiaidh sin, stopann gach rud ar feadh an dara ceann, ach anois ní fhilleann DelayAndgetRandom (1000) ar rud ar bith toisc nach bhfuil gá leis.
  • Mar thoradh air sin, ríomhtar na luachanna ag baint úsáide as an bhfoirmle A + B * c. Tá an toradh fillte i ngealltanas ag baint úsáide as promise.Resolve agus cuirtear ar ais é leis an bhfeidhm.

D'fhéadfadh na sosanna seo a bheith i gcuimhne do ghineadóirí in ES6, ach tá rud éigin ag baint leis Do chúiseanna.

Réiteach na faidhbe

Bhuel, déanaimis féachaint anois ar an réiteach ar an bhfadhb a luaitear thuas.

Úsáideann an fheidhm finishMyTask Fan le fanacht ar thorthaí oibríochtaí ar nós queryDatabase, sendEmail, logTaskInFile, agus cinn eile. Má dhéanann tú comparáid idir an réiteach seo agus an réiteach inar úsáideadh gealltanais, beidh na cosúlachtaí soiléir. Déanann an leagan Async/Await, áfach, na castachtaí comhréire go léir a shimpliú go mór. Sa chás seo, níl aon líon mór aisghlaonna agus slabhraí cosúil le .then/.catch.

Seo réiteach leis an aschur na n-uimhreacha, tá dhá rogha.

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

Agus seo réiteach ag baint úsáide as feidhmeanna async.

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

Earráid a phróiseáil

Tá earráidí gan láimhseáil fillte i ngeallúint diúltaithe. Mar sin féin, is féidir le feidhmeanna sioncronaithe iarracht / gabháil a úsáid chun earráidí a láimhseáil go sioncronach.

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

Is feidhm asincrónach é canRejectOrReturn() a éiríonn léi (“uimhir foirfe”) nó a dteipeann uirthi le hearráid (“Tá brón orm, tá an uimhir rómhór”).

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

Ós rud é go bhfuil an sampla thuas ag súil le canrejectorretrurn a fhorghníomhú, mar thoradh ar a theip féin, déanfar an bloc gabhála a fhorghníomhú. Mar thoradh air sin, críochnóidh an fheidhm Foo le ceachtar neamhshainithe (nuair a chuirtear aon rud ar ais sa bhloc triail) nó le hearráid gafa. Mar thoradh air sin, ní theipeann ar an bhfeidhm seo toisc go láimhseálfaidh an iarracht/ghabháil an fheidhm féin.

Seo sampla eile:

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

Is fiú aird a thabhairt ar an bhfíric go bhfuil CanRejectOrReturn ar ais ó foo sa sampla. Cuireann Foo sa chás seo deireadh le huimhir foirfe nó cuireann sé Earráid ar ais (“Tá brón orm, tá an uimhir rómhór”). Ní dhéanfar an bloc gabhála choíche.

Is í an fhadhb atá ann go dtugann foo an gealltanas a ritheadh ​​ó canRejectOrReturn ar ais. Mar sin is é an réiteach ar foo ná an réiteach ar canRejectOrReturn. Sa chás seo, ní bheidh sa chód ach dhá líne:

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

Seo cad a tharlóidh má úsáideann tú fanacht agus filleadh le chéile:

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

Sa chód thuas, beidh Foo ag imeacht go rathúil le huimhir foirfe agus earráid a ghabhtar. Ní bheidh aon diúltú anseo. Ach tiocfaidh Foo ar ais le CanRejectorretrurn, gan a bheith neamhshainithe. Déanaimis cinnte de seo trí dheireadh a chur leis an tuairisceán ag fanacht le líne canrejectorretrurn ():

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

Botúin agus gaistí coitianta

I gcásanna áirithe, d’fhéadfadh earráidí a bheith mar thoradh ar úsáid Async/Await.

Dearmadta ag fanacht

Tarlaíonn sé seo go minic go minic - déantar dearmad ar an eochairfhocal atá ag fanacht roimh an ngealltanas:

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

Mar a fheiceann tú, níl aon fanacht nó filleadh sa chód. Mar sin imíonn foo i gcónaí gan sainmhíniú gan mhoill 1 soicind. Ach beidh an gealltanas a chomhlíonadh. Má chaitheann sé earráid nó diúltú, cuirfear UnhandledPromiseRejectionWarning ar a dtugtar.

Feidhmeanna Asioncronacha in Aisghlaonna

Is minic a úsáidtear feidhmeanna async i .Map nó. Sampla is ea an fheidhm FetchPublicReposcount (ainm úsáideora), a fhilleann ar líon na stórtha oscailte ar GitHub. Ligean le rá go bhfuil triúr úsáideoirí a bhfuil a méadrachtaí ag teastáil uainn. Seo é an cód don tasc seo:

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

Teastaíonn cuntais ArfatSalman, octocat, norvig uainn. Sa chás seo déanaimid:

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

Is fiú aird a thabhairt ar fanacht ar ais. Is é atá i gceist leis seo ná sraith geallúintí, agus is aisghlaoch gan ainm é Map do gach úsáideoir sonraithe.

Úsáid ró-chomhsheasmhach as fanacht

Glacaimis an cód seo mar shampla:

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

Anseo cuirtear an uimhir repo san athróg comhaireamh, ansin cuirtear an uimhir seo leis an eagar comhaireamh. Is é an fhadhb leis an gcód ná go mbeidh gach úsáideoir ina dhiaidh sin i mód fuireachais go dtí go dtagann sonraí an chéad úsáideora ón bhfreastalaí. Mar sin, ní dhéantar ach úsáideoir amháin a phróiseáil ag an am.

Más rud é, mar shampla, go dtógann sé thart ar 300 ms úsáideoir amháin a phróiseáil, ansin do na húsáideoirí go léir tá sé an dara ceann cheana féin; braitheann an t -am a chaitear go líneach ar líon na n -úsáideoirí. Ach ós rud é nach mbraitheann líon na n -repo ar a chéile, is féidir na próisis a chomhthreomhar. Éilíonn sé seo oibriú le .map agus Promise.all:

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

Geallta. Faigheann gach duine gealltanais mar ionchur agus cuireann sé gealltanas ar ais. Tá an dara ceann, tar éis gach gealltanas san eagar críochnaithe nó ag an gcéad dhiúltú, críochnaithe. B'fhéidir go dtarlóidh sé nach dtosaíonn siad go léir ag an am céanna - chun tús comhuaineach a chinntiú, is féidir leat P -MAP a úsáid.

Conclúid

Tá feidhmeanna async ag éirí níos tábhachtaí le haghaidh forbartha. Bhuel, le haghaidh úsáid oiriúnaitheach feidhmeanna async, ba cheart duit úsáid a bhaint as Async Iterators. Ba chóir go mbeadh forbróir JavaScript eolach ar seo.

Molann Skillbox:

Foinse: will.com

Add a comment