Makhe sijonge kwi-Async/Linda kwiJavaScript sisebenzisa imizekelo

Umbhali wenqaku uhlola imizekelo ye-Async / Linda kwiJavaScript. Ngokubanzi, i-Async / Ukulinda yindlela efanelekileyo yokubhala ikhowudi ye-asynchronous. Ngaphambi kokuba eli nqaku livele, ikhowudi enjalo yabhalwa kusetyenziswa ukufowunelwa kunye nezithembiso. Umbhali wenqaku lokuqala ubonisa iingenelo ze-Async / Linda ngokuhlalutya imizekelo eyahlukeneyo.

Siyakhumbuza: kubo bonke abafundi be "Habr" - isaphulelo se-ruble ye-10 xa ubhalisa kuyo nayiphi na ikhosi ye-Skillbox usebenzisa ikhowudi yokuphromotha "Habr".

I-Skillbox iyacebisa: Isifundo esikwi-intanethi "Umphuhlisi weJava".

callback

Ukufowuna lumsebenzi umnxeba wawo ulibazisekile ngokungenasiphelo. Ngaphambili, ii-callbacks zazisetyenziswa kwezo ndawo zekhowudi apho umphumo wawungenakufumaneka ngokukhawuleza.

Nanku umzekelo wokufunda ngokungahambelaniyo ifayile kwi-Node.js:

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

Iingxaki zivela xa ufuna ukwenza imisebenzi emininzi engatshintshiyo ngaxeshanye. Makhe sicinge ngale meko: isicelo senziwe kwisiseko sedatha somsebenzisi weArfat, kufuneka ufunde iprofayile_img_url indawo kwaye ukhuphele umfanekiso kwiseva ye-someserver.com.
Emva kokukhuphela, siguqula umfanekiso kwenye ifomathi, umzekelo ukusuka kwi-PNG ukuya kwiJPEG. Ukuba ukuguqulwa kuphumelele, ileta ithunyelwa kwi-imeyile yomsebenzisi. Emva koko, ulwazi malunga nesiganeko lufakwe kwifayile yenguqu.log, ebonisa umhla.

Kufanelekile ukunikela ingqalelo kwi-overlap of callbacks kunye nenani elikhulu le-}) kwinxalenye yokugqibela yekhowudi. Ibizwa ngokuba yiCallback Hell okanye iPiramidi yeDoom.

Ukungalungi kwale ndlela kucacile:

  • Le khowudi kunzima ukuyifunda.
  • Kukwanzima nokuphatha iimpazamo, nto leyo edla ngokukhokelela kumgangatho ophantsi wekhowudi.

Ukusombulula le ngxaki, izithembiso zongezwa kwiJavaScript. Zikuvumela ukuba ubuyisele indlwane enzulu yee-callbacks ngegama .ngoko.

Inkalo entle yezithembiso kukuba benza ukuba ikhowudi ifundeke ngcono, ukusuka phezulu ukuya ezantsi kunokuba ukusuka kwesobunxele ukuya ekunene. Nangona kunjalo, izithembiso nazo zineengxaki zazo:

  • Kufuneka udibanise okuninzi .ngoko.
  • Endaweni yokuzama/ukubamba, .ukubamba kusetyenziselwa ukuphatha zonke iimpazamo.
  • Ukusebenza ngezithembiso ezininzi ngaphakathi kwilophu enye akusoloko kulungele; kwezinye iimeko, bayayidibanisa ikhowudi.

Nantsi ingxaki eza kubonisa intsingiselo yenqaku lokugqibela.

Masithi sine-loop eprinta ulandelelwano lwamanani ukusuka ku-0 ukuya ku-10 ngamaxesha athile (imizuzwana eyi-0-n). Usebenzisa izithembiso, kufuneka utshintshe le loop ukuze amanani aprintwe ngokulandelelana ukusuka ku-0 ukuya ku-10. Ngoko ke, ukuba kuthatha imizuzwana emi-6 ukuprinta unothi kunye nemizuzwana emi-2 ukuprinta enye, unoziro kufuneka aprintwe kuqala, kwaye emva koko. ukubala ukwehla ngokuprinta enye iyakuqala.

Kwaye kunjalo, asisebenzisi i-Async/Await okanye .sort ukusombulula le ngxaki. Isisombululo somzekelo sisekupheleni.

Imisebenzi yeAsync

Ukongezwa kwemisebenzi ye-async kwi-ES2017 (ES8) yenze lula umsebenzi wokusebenza kunye nezithembiso. Ndiqaphela ukuba imisebenzi ye-async isebenza "phezulu" yezithembiso. Le misebenzi ayimeli ngokomgangatho iingqiqo ezahlukeneyo. Imisebenzi ye-Async yenzelwe njengenye indlela kwikhowudi esebenzisa izithembiso.

I-Async/Await yenza kube lula ukulungelelanisa umsebenzi kunye nekhowudi engatshintshiyo kwisitayile esihambelanayo.

Ngaloo ndlela, ukwazi izithembiso kwenza kube lula ukuqonda imigaqo ye-Async/Await.

I-Syntax

Ngokwesiqhelo iqulathe amagama angundoqo amabini: async kwaye ulinde. Igama lokuqala lijika umsebenzi ube ongahambelaniyo. Imisebenzi enjalo ivumela ukusetyenziswa kokulinda. Kuyo nayiphi na enye imeko, ukusebenzisa lo msebenzi kuya kuvelisa impazamo.

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

I-Async ifakwe ekuqaleni komsebenzi wokubhengeza, kwaye kwimeko yotolo olusebenzayo, phakathi kwe "=" uphawu kunye nezibiyeli.

Le misebenzi inokufakwa kwinto njengeendlela okanye isetyenziswe kwisibhengezo seklasi.

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

QAPHELA! Kuyafaneleka ukukhumbula ukuba abakhi beklasi kunye nee-getters/setters azikwazi ukuhambelana.

ISemantics kunye nemithetho yokwenziwa

Imisebenzi ye-Async ifana ngokusisiseko nemisebenzi ye-JS eqhelekileyo, kodwa kukho izinto ezingaphandle.

Ke, imisebenzi ye-async ihlala ibuyisela izithembiso:

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

Ngokukodwa, i-fn ibuyisela umtya molo. Kulungile, kuba lo ngumsebenzi ongena-synchronous, ixabiso lomtya lisongelwe kwisithembiso kusetyenziswa umakhi.

Nalu olunye uyilo ngaphandle kwe-Async:

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

Kule meko, isithembiso sibuyiselwa "ngesandla". Umsebenzi we-asynchronous uhlala usongelwe kwisithembiso esitsha.

Ukuba ixabiso lembuyekezo yeyelo, umsebenzi we-async ubuyisela ixabiso ngokulisonga kwisithembiso. Ukuba ixabiso lokubuyisela liyinto yesithembiso, isisombululo sayo sibuyiselwa kwisithembiso esitsha.

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

Kodwa kwenzeka ntoni ukuba kukho impazamo ngaphakathi komsebenzi ongahambelaniyo?

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

Ukuba ayilungiswanga, i-foo() izakubuyisela isithembiso ngokukhatywa. Kule meko, i-Promise.reject equlethe impazamo iya kubuyiselwa endaweni ye-Promise.resolve.

Imisebenzi ye-Async ihlala ivelisa isithembiso, nokuba yintoni na ebuyiselweyo.

Imisebenzi yeAsynchronous iyanqumama kuyo yonke into elindelweyo .

Ukulinda kuchaphazela intetho. Ke, ukuba ibinzana lisisithembiso, umsebenzi we-async uyanqunyanyiswa de isithembiso sizaliseke. Ukuba intetho ayisosithembiso, iguqulelwa kwisithembiso nge-Promise.resolve kwaye igqitywe.

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

Kwaye nantsi inkcazo yendlela umsebenzi wefn osebenza ngayo.

  • Emva kokuyibiza, umgca wokuqala uguqulwa kwi-const a = ulinde i-9; in const a = linda isithembiso.sombulule(9);.
  • Emva kokusebenzisa u-Lindela, ukuphunyezwa komsebenzi kunqunyanyisiwe de ifumane ixabiso layo (kwimeko yangoku yi-9).
  • UkulibazisekaAndGetRandom(1000) kumisa ukuphunyezwa komsebenzi wefn ide igqibezele ngokwawo (emva komzuzwana omnye). Oku kumisa ngempumelelo umsebenzi wefn kwisekhondi enye.
  • UkulibazisekaAndGetRandom(1000) ngokusonjululwa ibuyisela ixabiso elingenamkhethe, eliye labelwa kutshintsho b.
  • Kulungile, imeko enokuguquguquka kuka c iyafana nemeko enoguqulo a. Emva koko, yonke into iyama umzuzwana, kodwa ngoku ilibazisaAndGetRandom(1000) ayibuyisi nto kuba ayifunwa.
  • Ngenxa yoko, amaxabiso abalwa kusetyenziswa ifomula a + b * c. Isiphumo esongelwe kwisithembiso usebenzisa i-Promise.resolve kwaye ibuyiselwe ngumsebenzi.

Oku nqumama kunokuba sisikhumbuzo sejenereyitha kwi-ES6, kodwa kukho into apho izizathu zakho.

Ukusombulula ingxaki

Ewe, ngoku makhe sijonge isisombululo kwingxaki ekhankanywe ngasentla.

Umsebenzi we finishMyTask usebenzisa i-Await ukulinda iziphumo zemisebenzi efana ne-queryDatabase, sendEmail, logTaskInFile, kunye nabanye. Ukuba uthelekisa esi sicombululo kunye naleyo apho kwakusetyenziswe izithembiso, ukufana kuya kubonakala. Nangona kunjalo, inguqulelo ye-Async/Await yenza lula zonke izinto ezintsonkothileyo. Kule meko, akukho nani likhulu lokufowuna kunye namatyathanga afana .then/.catch.

Nasi isisombululo kunye nesiphumo samanani, kukho iinketho ezimbini.

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

Kwaye nasi isisombululo usebenzisa imisebenzi ye-async.

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

Uqhubekeko lwempazamo

Iimpazamo ezingasingathwanga zisongelwe kwisithembiso esikhatywayo. Nangona kunjalo, imisebenzi ye-async inokusebenzisa itrayi/ukubamba ukusingatha iimpazamo ngolungelelwano.

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() ngumsebenzi ongahambelaniyo onokuthi uphumelele (“inombolo egqibeleleyo”) okanye usilele ngempazamo (“Uxolo, inani likhulu kakhulu”).

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

Kuba umzekelo ongasentla ulindele ukuba iCanRejectOrReturn iphumeze, ukusilela kwayo kuya kubangela ukuphunyezwa kwebhloko yokubambisa. Ngenxa yoko, umsebenzi foo uyakuphela nokuba kungachazwanga (xa kungekho nto ibuyiswayo kwibhloko yokuzama) okanye ngempazamo ebanjiweyo. Ngenxa yoko, lo msebenzi awuyi kusilela kuba itrayi/ukubamba izakusingatha umsebenzi foo ngokwawo.

Nanku omnye umzekelo:

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

Kufanelekile ukunikela ingqalelo kwinto yokuba kumzekelo, i-canRejectOrReturn ibuyiswa kwi-foo. I-Fo kulo mzekelo inokuphela ngenani eligqibeleleyo okanye ibuyise imposiso (“Uxolo, inani elikhulu kakhulu”). Ibhloko yokubamba ayinakuze iphunyezwe.

Ingxaki kukuba i-foo ibuyisela isithembiso esigqithiswe kwi-canRejectOrReturn. Ke isisombululo kwi foo siba sisisombululo kwi canRejectOrReturn. Kule meko, ikhowudi iya kuba nemigca emibini kuphela:

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

Nantsi into eyenzekayo ukuba usebenzisa i-wait kwaye ubuye kunye:

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

Kwikhowudi engentla, i-foo iya kuphuma ngempumelelo kunye nenani eligqibeleleyo kunye nempazamo ebanjiweyo. Akusayi kubakho kwaliwe apha. Kodwa i-foo iza kubuya nge canRejectOrReturn, hayi nengachazwanga. Masiqinisekise ngoku ngokususa umgca wokubuyela ulinde canRejectOrReturn():

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

Iimpazamo eziqhelekileyo kunye nemigibe

Kwezinye iimeko, ukusebenzisa i-Async/Await kunokukhokelela kwiimpazamo.

Ulibele ulinde

Oku kwenzeka rhoqo-igama elingundoqo lokulinda liyalibala phambi kwesithembiso:

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

Njengoko ubona, akukho kulinda okanye ukubuya kwikhowudi. Ke ngoko i-foo ihlala iphuma ngokungachazwanga ngaphandle kokulibaziseka komzuzwana omnye. Kodwa idinga liya kuzaliseka. Ukuba iphosa imposiso okanye ukwaliwa, ngoko UnhandledPromiseRejectionWarning iyakubizwa.

Imisebenzi ye-Async kwii-Callbacks

Imisebenzi yeAsync isetyenziswa rhoqo kwimaphu okanye .filtha njengofowunelwa. Umzekelo yi fetchPublicReposCount(igama lomsebenzisi) umsebenzi, obuyisela inani leendawo zokugcina ezivuliweyo kwiGitHub. Masithi kukho abasebenzisi abathathu esizifunayo iimetrikhi zabo. Nantsi ikhowudi yalo msebenzi:

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

Sifuna i-ArfatSalman, i-octocat, ii-akhawunti ze-norvig. Kule meko senza:

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

Kufanelekile ukunikela ingqalelo ku-Linda kwi-callback yemephu. Apha ubalo luthotho lwezithembiso, kwaye .maphu yi-callback engaziwa kumsebenzisi ngamnye okhankanyiweyo.

Ukusetyenziswa okuhambelanayo ngokugqithisileyo kokulinda

Masithathe le khowudi njengomzekelo:

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

Apha inombolo ye-repo ibekwe kwi-count variable, kwaye eli nani lidityaniswe kuluhlu lwamanani. Ingxaki ngekhowudi kukuba de idatha yomsebenzisi wokuqala ifike kwiseva, bonke abasebenzisi abalandelayo baya kuba kwimodi yokulinda. Ngaloo ndlela, umsebenzisi omnye kuphela ocutshungulwayo ngexesha.

Ukuba, umzekelo, kuthatha malunga ne-300 ms ukuqhubekekisa umsebenzisi omnye, ngoko kubo bonke abasebenzisi sele iyisibini; ixesha elichithwe ngokomgca lixhomekeke kwinani labasebenzisi. Kodwa ekubeni ukufumana inani le-repo akuxhomekanga omnye komnye, iinkqubo zinokudityaniswa. Oku kufuna ukusebenza nge.map kunye ne-Promise.all:

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

I-Promise.all ifumana uluhlu lwezithembiso njengegalelo kwaye ibuyisela isithembiso. Esokugqibela, emva kokuba zonke izithembiso ezikuluhlu zigqityiwe okanye ekukhatyweni kokuqala, zigqityiwe. Kungenzeka ukuba zonke zingaqali ngexesha elinye - ukuze uqinisekise ukuqala ngaxeshanye, ungasebenzisa i-p-map.

isiphelo

Imisebenzi ye-Async iya ibaluleke kakhulu kuphuhliso. Ewe, kusetyenziso oluguqukayo lwemisebenzi ye-async, kufuneka usebenzise Async Iterators. Umphuhlisi weJavaScript kufuneka ayazi kakuhle le nto.

I-Skillbox iyacebisa:

umthombo: www.habr.com

Yongeza izimvo