Bari mu kalli Async/A jira a cikin JavaScript ta amfani da misalai

Marubucin labarin yayi nazarin misalan Async/Await a cikin JavaScript. Gabaɗaya, Async/Await hanya ce mai dacewa don rubuta lambar asynchronous. Kafin wannan fasalin ya bayyana, an rubuta irin wannan lambar ta amfani da sake kira da alƙawura. Marubucin ainihin labarin ya bayyana fa'idodin Async/Await ta hanyar nazarin misalai daban-daban.

Muna tunatarwa: ga duk masu karatu na "Habr" - rangwame na 10 rubles lokacin yin rajista a kowane kwas na Skillbox ta amfani da lambar talla "Habr".

Skillbox yana ba da shawarar: Ilimin kan layi kwas "Java developer".

callback

Kiran dawowa aiki ne wanda kiran sa ke jinkiri har abada. A baya can, an yi amfani da sake kiran waya a waɗancan wuraren lambar inda ba za a iya samun sakamakon nan da nan ba.

Ga misali na karanta fayil ba tare da izini ba a Node.js:

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

Matsaloli suna tasowa lokacin da kake buƙatar aiwatar da ayyuka da yawa na asynchronous lokaci guda. Bari mu yi tunanin wannan yanayin: an yi buƙatu zuwa ga bayanan mai amfani da Arfat, kuna buƙatar karanta filin profile_img_url kuma ku zazzage hoto daga uwar garken someserver.com.
Bayan saukarwa, muna canza hoton zuwa wani tsari, misali daga PNG zuwa JPEG. Idan canjin ya yi nasara, ana aika wasiƙa zuwa imel ɗin mai amfani. Bayan haka, an shigar da bayanai game da taron a cikin fayil ɗin transformations.log, yana nuna kwanan wata.

Yana da daraja a kula da zoba na kiran baya da kuma babban adadin }) a cikin ɓangaren ƙarshe na lambar. Ana kiran shi Callback Jahannama ko Pyramid of Doom.

Rashin amfanin wannan hanyar a bayyane yake:

  • Wannan lambar tana da wahalar karantawa.
  • Hakanan yana da wahala a iya magance kurakurai, wanda galibi yana haifar da rashin ingancin lambar.

Don magance wannan matsalar, an ƙara alƙawura zuwa JavaScript. Suna ba ka damar maye gurbin zurfafan gida na kiran baya da kalmar .sannan.

Kyakkyawan al'amari na alkawuran shi ne cewa suna sa lambar ta fi dacewa da karantawa, daga sama zuwa kasa maimakon daga hagu zuwa dama. Duk da haka, alƙawura kuma suna da matsalolinsu:

  • Kuna buƙatar ƙara da yawa .sannan.
  • Maimakon gwadawa / kama, ana amfani da .catch don magance duk kurakurai.
  • Yin aiki tare da alƙawura da yawa a cikin madauki ɗaya ba koyaushe dace ba; a wasu lokuta, suna rikitar da lambar.

Ga matsalar da za ta nuna ma'anar batu na ƙarshe.

A ce muna da madauki don buga jerin lambobi daga 0 zuwa 10 a tsaka-tsakin bazuwar (0–n seconds). Yin amfani da alƙawura, kuna buƙatar canza wannan madauki don buga lambobin a jere daga 0 zuwa 10. Don haka, idan ya ɗauki daƙiƙa 6 don buga sifili da sakan 2 don buga ɗaya, yakamata a fara buga sifilin, sannan za a fara kirga bugu na daya.

Kuma ba shakka, ba ma amfani da Async/Await ko .sort don magance wannan matsalar. Misalin bayani shine a ƙarshe.

Ayyukan Async

Ƙarin ayyukan async a cikin ES2017 (ES8) ya sauƙaƙe aikin aiki tare da alkawuran. Na lura cewa ayyukan async suna aiki "a saman" alkawuran. Waɗannan ayyuka ba sa wakiltar ra'ayoyi daban-daban na inganci. Ayyukan Async an yi niyya azaman madadin lambar da ke amfani da alkawuran.

Async/Await yana ba da damar tsara aiki tare da lambar asynchronous a cikin salon aiki tare.

Don haka, sanin alkawuran yana sa sauƙin fahimtar ƙa'idodin Async/Await.

ginin kalma

A al'ada ya ƙunshi kalmomi guda biyu: async da jira. Kalmar farko tana juya aikin zuwa asynchronous. Irin waɗannan ayyuka suna ba da damar amfani da jira. A kowane hali, yin amfani da wannan aikin zai haifar da kuskure.

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

Ana shigar da Async a farkon sanarwar aikin, kuma a cikin yanayin aikin kibiya, tsakanin alamar "=" da ƙira.

Ana iya sanya waɗannan ayyuka a cikin wani abu azaman hanyoyi ko amfani da su a cikin sanarwar aji.

// 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! Yana da kyau a tuna cewa masu ginin ajin da masu gyarawa ba za su iya zama asynchronous ba.

Semantics da dokokin kisa

Ayyukan Async suna kama da daidaitattun ayyukan JS, amma akwai keɓantacce.

Don haka, ayyukan async koyaushe suna dawo da alkawuran:

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

Musamman, fn yana dawo da kirtani sannu. Da kyau, tunda wannan aikin asynchronous ne, ƙimar kirtani tana naɗe cikin alkawari ta amfani da mai gini.

Ga madadin ƙira ba tare da Async ba:

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

A wannan yanayin, ana mayar da wa'adin "da hannu". Aikin asynchronous koyaushe yana nannade cikin sabon alkawari.

Idan darajar dawowar ta zama na farko, aikin async yana dawo da ƙimar ta hanyar nannade shi a cikin alkawari. Idan darajar dawowar abu ne na alkawari, za a dawo da ƙudurinsa a cikin sabon alkawari.

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

Amma menene zai faru idan akwai kuskure a cikin aikin asynchronous?

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

Idan ba a sarrafa shi ba, foo() zai dawo da alkawari tare da ƙin yarda. A wannan yanayin, za a dawo da Alƙawari.reject ɗin da ke ɗauke da kuskure maimakon Alƙawarin.resolve.

Ayyukan Async koyaushe suna fitar da alkawari, ba tare da la'akari da abin da aka dawo ba.

Ayyukan Asynchronous suna tsayawa akan kowane jira.

Jira yana rinjayar maganganu. Don haka, idan maganganun alkawari ne, an dakatar da aikin async har sai an cika alkawari. Idan furcin ba alkawari ba ne, an canza shi zuwa alkawari ta hanyar Promise.resolve sannan a kammala.

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

Kuma ga bayanin yadda aikin fn ke aiki.

  • Bayan kiransa, layin farko yana canzawa daga const a = jira 9; in const a = jira Alkawari.resolve(9);.
  • Bayan amfani da Await, ana dakatar da aiwatar da aikin har sai an sami ƙimar sa (a halin da ake ciki yanzu shine 9).
  • delayAndGetRandom(1000) yana dakatar da aiwatar da fn ɗin har sai ya kammala kansa (bayan 1 seconds). Wannan yana dakatar da aikin fn yadda ya kamata na daƙiƙa 1.
  • jinkiriAndGetRandom(1000) ta hanyar warwarewa yana dawo da ƙima bazuwar, wanda sannan aka sanya shi zuwa madaidaicin b.
  • To, yanayin da m c ya yi kama da yanayin da m a. Bayan haka, komai yana tsayawa na daƙiƙa guda, amma yanzu jinkiriAndGetRandom(1000) baya dawo da komai saboda ba a buƙata ba.
  • A sakamakon haka, ana ƙididdige ƙimar ta amfani da dabarar a + b * c. Sakamakon yana kunshe a cikin alkawari ta amfani da Promise.resolve kuma ya dawo da aikin.

Waɗannan tsaikon na iya zama abin tunawa da janareta a cikin ES6, amma akwai wani abu gare shi dalilan ku.

Magance matsalar

To, yanzu bari mu duba yadda za a magance matsalar da aka ambata a sama.

Aikin gamaMyTask yana amfani da Jira don jira sakamakon ayyuka kamar su questionDatabase, sendEmail, logTaskInFile, da sauransu. Idan aka kwatanta wannan bayani da wanda aka yi amfani da alkawuran, kamanceceniya za su bayyana. Koyaya, sigar Async/Await tana sauƙaƙa da duk rikitattun haɗin gwiwa. A wannan yanayin, babu adadi mai yawa na kira baya da sarƙoƙi kamar .sannan / .catch.

Anan akwai mafita tare da fitar da lambobi, akwai zaɓuɓɓuka biyu.

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

Kuma a nan akwai mafita ta amfani da ayyukan async.

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

Kuskuren sarrafawa

Kurakurai da ba a sarrafa su an nannade su cikin alkawarin da aka ƙi. Koyaya, ayyukan async na iya amfani da gwadawa/kama don magance kurakurai tare.

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() aiki ne na asynchronous wanda ko dai yayi nasara ("cikakkiyar lamba") ko ta kasa tare da kuskure ("Yi hakuri, lamba yayi girma").

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

Tunda misalin da ke sama yana tsammanin zai iyaRejectOrReturn don aiwatarwa, gazawar nasa zai haifar da aiwatar da shingen kama. Sakamakon haka, aikin foo zai ƙare tare da ko dai ba a bayyana shi ba (lokacin da ba a dawo da kome ba a cikin toshewar gwaji) ko tare da kuskuren kama. Sakamakon haka, wannan aikin ba zai gaza ba saboda gwadawa/kamawa zai kula da aikin foo kanta.

Ga wani misali:

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

Yana da kyau a kula da gaskiyar cewa a cikin misali, canRejectOrReturn yana dawowa daga foo. Foo a wannan yanayin ko dai ya ƙare da cikakkiyar lamba ko kuma ya dawo da Kuskure ("Yi hakuri, lamba ta yi girma"). Ba za a taɓa aiwatar da shingen kamawa ba.

Matsalar ita ce foo ya dawo da alkawarin da aka yi daga canRejectOrReturn. Don haka maganin foo ya zama mafita ga canRejectOrReturn. A wannan yanayin, lambar za ta ƙunshi layi biyu kawai:

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

Ga abin da zai faru idan kun yi amfani da jira ku dawo tare:

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

A cikin lambar da ke sama, foo zai fita cikin nasara tare da cikakkiyar lamba da kuskuren kama. Ba za a ƙi a nan ba. Amma foo zai dawo tare da canRejectOrReturn, ba tare da wanda ba a bayyana ba. Bari mu tabbatar da wannan ta hanyar cire dawowar jiran canRejectOrReturn() layin:

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

Kuskure na gama gari da ramuka

A wasu lokuta, amfani da Async/Await na iya haifar da kurakurai.

An manta jira

Wannan yana faruwa sau da yawa - ana manta kalmar jira kafin alƙawarin:

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

Kamar yadda kuke gani, babu jira ko dawowa a cikin lambar. Saboda haka foo koyaushe yana fita ba tare da an bayyana shi ba tare da jinkirin daƙiƙa 1 ba. Amma alkawarin zai cika. Idan ya jefa kuskure ko ƙi, to UnhandledPromiseRejectionWarning za a kira.

Ayyukan Async a cikin Kira

Ana yawan amfani da ayyukan Async a .taswira ko .tace azaman dawo da kira. Misali shine aikin fetchPublicReposCount(sunan mai amfani), wanda ke dawo da adadin buɗaɗɗen ma'ajiyar a GitHub. Bari mu ce akwai masu amfani guda uku waɗanda muke buƙatar awo. Ga lambar wannan aikin:

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

Muna bukatar ArfatSalman, octocat, norvig asusu. A wannan yanayin muna yin:

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

Yana da kyau a kula da jira a cikin .map callback. Anan kirga akwai tarin alƙawura, kuma .taswira shine sake kiran da ba a bayyana ba ga kowane takamaiman mai amfani.

Yin amfani da jira sosai akai-akai

Bari mu dauki wannan lambar a matsayin misali:

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

Anan an sanya lambar repo a cikin ma'aunin ƙidaya, sannan ana ƙara wannan lambar zuwa jerin ƙididdiga. Matsalar lambar ita ce har sai bayanan mai amfani na farko ya zo daga uwar garken, duk masu amfani da ke gaba za su kasance cikin yanayin jiran aiki. Don haka, mai amfani ɗaya ne kawai ake sarrafa shi a lokaci ɗaya.

Idan, alal misali, yana ɗaukar kusan 300 ms don aiwatar da mai amfani ɗaya, to ga duk masu amfani ya riga ya zama na biyu; lokacin da aka kashe a layi ya dogara da adadin masu amfani. Amma tun da samun adadin repo bai dogara da juna ba, ana iya daidaita tsarin. Wannan yana buƙatar aiki tare da .map da Promise.all:

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

Alkawari.dukkanin suna karɓar alƙawura da yawa a matsayin shigarwa kuma suna dawo da alkawari. Na ƙarshe, bayan duk alkawuran da ke cikin tsararru sun cika ko a farkon ƙin yarda, an cika su. Yana iya faruwa cewa duka ba su farawa lokaci guda - don tabbatar da farawa lokaci guda, kuna iya amfani da p-map.

ƙarshe

Ayyukan Async suna ƙara zama mahimmanci don ci gaba. Da kyau, don daidaita amfani da ayyukan async, ya kamata ku yi amfani da su Async Iterators. Ya kamata mai haɓaka JavaScript ya kware sosai a wannan.

Skillbox yana ba da shawarar:

source: www.habr.com

Add a comment