Ka em bi mînakan li Async/Await di JavaScriptê de binêrin

Nivîskarê gotarê mînakên Async / Await di JavaScript de lêkolîn dike. Bi tevahî, Async / Await ji bo nivîsandina koda asynchronous awayek hêsan e. Berî ku ev taybetmendî xuya bibe, kodek weha bi karanîna bang û sozan hate nivîsandin. Nivîskarê gotara orjînal bi analîzkirina mînakên cihêreng avantajên Async/Await eşkere dike.

Em bînin bîra xwe: ji bo hemî xwendevanên "Habr" - dema ku hûn beşdarî qursek Skillbox-ê bi karanîna koda danasînê ya "Habr" têne qeyd kirin 10 rubleyan dakêşin.

Skillbox pêşniyar dike: Kursa perwerdehiya serhêl "pêşdebirê Java".

Callback

Callback fonksiyonek e ku banga wê ji bo demek nediyar tê dereng kirin. Berê, paşvekişandin li wan deverên kodê dihatin bikar anîn ku encam tavilê nedihat bidestxistin.

Li vir mînakek xwendina asynkron a pelek li Node.js heye:

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

Pirsgirêk dema ku hûn hewce ne ku bi yekcarî çend operasyonên asynchronous bikin derdikevin. Ka em vê senaryoyê xeyal bikin: daxwazek ji databasa bikarhênerê Arfat re tê kirin, hûn hewce ne ku zeviya wê profile_img_url bixwînin û wêneyek ji servera someserver.com dakêşin.
Piştî dakêşandinê, em wêneyê veguherînin formatek din, mînakî ji PNG bo JPEG. Ger veguhertin serketî bû, nameyek ji e-nameya bikarhêner re tê şandin. Piştre, agahdariya di derbarê bûyerê de têkevin pelê transformations.log, ku tarîxê destnîşan dike.

Hêjayî gotinê ye ku di beşa paşîn a kodê de balê bikişîne ser hevberdana bangawaziyan û hejmareke mezin a }). Jê re dibêjin Callback Hell an Pyramid of Doom.

Dezawantajên vê rêbazê diyar in:

  • Ev kod zehmet e ku were xwendin.
  • Di heman demê de dijwar e ku meriv bi xeletiyan re mijûl bibe, ku pir caran dibe sedema kalîteya kodê ya xirab.

Ji bo çareserkirina vê pirsgirêkê, soz li JavaScriptê hatin zêdekirin. Ew dihêlin ku hûn hêlînên kûr ên bangewaziyê bi peyva .paşê biguhezînin.

Aliyê erênî yên sozan ev e ku ew kodê ji jor ber bi jêr ve ji çepê ber bi rastê pir çêtir xwendinê dikin. Lêbelê, soz jî pirsgirêkên wan hene:

  • Pêdivî ye ku hûn pir zêde bikin .paşê.
  • Li şûna hewl/catch, .catch tê bikaranîn ku hemî xeletiyan bi rê ve bibe.
  • Karkirina bi gelek sozên di hundurê yek loopê de her gav ne hêsan e; di hin rewşan de, ew kodê tevlihev dikin.

Li vir pirsgirêkek heye ku dê wateya xala dawîn nîşan bide.

Bifikirin ku me lekeyek for heye ku rêzek hejmarên ji 0 heya 10-an bi navberên bêserûber (0-n çirkeyan) çap dike. Bi karanîna sozan, pêdivî ye ku hûn vê lûkê biguhezînin da ku hejmar ji 0 heya 10-an li rêzê werin çap kirin. Ji ber vê yekê, heke çapkirina sifirek 6 saniye û çapkirina yekê jî 2 çirk digire, divê pêşî sifir were çap kirin û dûv re. ji bo çapkirinê jimartin wê dest pê bike.

Û bê guman, em ji bo çareserkirina vê pirsgirêkê Async/Await an .sort bikar naynin. Mînaka çareseriyê di dawiyê de ye.

Fonksiyonên Async

Zêdekirina fonksiyonên async di ES2017 (ES8) de karê xebata bi sozan re hêsan kir. Ez destnîşan dikim ku fonksiyonên async "li ser" sozan dixebitin. Van fonksiyonan têgînên bi kalîte cuda temsîl nakin. Fonksiyonên Async wekî alternatîfek koda ku soz bikar tîne têne armanc kirin.

Async/Await îmkana organîzekirina xebata bi koda asynchronous bi şêwazek hevdem dike.

Ji ber vê yekê, zanîna sozan famkirina prensîbên Async/Await hêsantir dike.

syntax

Bi gelemperî ew ji du peyvan pêk tê: async û li bendê. Peyva yekem fonksiyonê dike asynchronous. Fonksiyonên bi vî rengî destûrê didin karanîna bendê. Di rewşek din de, karanîna vê fonksiyonê dê xeletiyek çêbike.

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

Async di destpêka danezana fonksiyonê de, û di rewşa fonksiyonek tîrê de, di navbera nîşana "=" û parantezê de tê danîn.

Van fonksiyonan dikarin di objeyekê de wekî rêbaz werin danîn an jî di danezanek polê de werin bikar anîn.

// 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! Hêjayî bibîrxistinê ye ku çêkerên sinifê û wergir/sazker nikarin asînkron bin.

Semantîk û qaîdeyên darvekirinê

Fonksiyonên Async di bingeh de mîna fonksiyonên JS standard in, lê îstîsna hene.

Bi vî rengî, fonksiyonên async her gav soz vedigerin:

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

Bi taybetî, fn rêzika hello vedigerîne. Welê, ji ber ku ev fonksiyonek asynkron e, nirxa rêzikê bi karanîna çêkerek di sozekê de tê pêçan.

Li vir sêwiranek alternatîf bêyî Async heye:

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

Di vê rewşê de, soz "bi destan" tê vegerandin. Fonksîyonek asynkron her gav di sozek nû de tê pêçan.

Ger nirxa vegerê primitive be, fonksiyona async bi pêçandina wê di sozekê de nirxê vedigerîne. Ger nirxa vegerê tiştek soz be, çareseriya wê di sozek nû de vedigere.

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

Lê heke di hundurê fonksiyonek asynchronous de xeletiyek hebe çi dibe?

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

Ger ew neyê pêvajo kirin, foo() dê sozek bi redkirinê vegerîne. Di vê rewşê de, dê li şûna Promise.resolve were vegerandin Promise.reject ku xeletiyek heye.

Fonksiyonên Async her gav sozek derdixin, bêyî ku çi were vegerandin.

Fonksiyonên asynchronous li ser her bendewariyê disekinin.

Li benda îfadeyan bandor dike. Ji ber vê yekê, heke îfade sozek be, fonksiyona async tê sekinandin heya ku soz pêk were. Ger îfade ne soz be, bi rêya Promise.resolve veguherî sozekê û paşê tê qedandin.

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

Û li vir ravekek heye ka fonksiyona fn çawa dixebite.

  • Piştî gazîkirina wê, rêza yekem ji const a = await 9 tê veguheztin; in const a = li benda Promise.resolve(9);.
  • Piştî karanîna Await, pêkanîna fonksiyonê tê sekinandin heya ku a nirxa xwe bigire (di rewşa heyî de ew 9 e).
  • delayAndGetRandom(1000) pêkanîna fonksiyona fn rawestîne heta ku ew xwe temam bike (piştî 1 çirkeyê). Ev bi bandor fonksiyona fn ji bo 1 seconds disekine.
  • delayAndGetRandom(1000) bi rêya çareseriyê nirxek random vedigerîne, ku dûv re ji guherbara b re tê destnîşankirin.
  • Belê, rewşa guherbara c dişibe rewşa guherbar a. Piştî wê, her tişt ji bo saniyeyekê disekine, lê naha delayAndGetRandom(1000) tiştek venagere ji ber ku ew ne hewce ye.
  • Wekî encamek, nirx bi karanîna formula a + b * c têne hesibandin. Encam di sozekê de bi karanîna Promise.resolve tê pêçan û ji hêla fonksiyonê ve tê vegerandin.

Dibe ku ev rawestan di ES6 de jeneratoran bînin bîra xwe, lê tiştek jê heye sedemên te.

Çareserkirina pirsgirêkê

Belê, naha em li çareseriya pirsgirêka ku li jor hatî destnîşan kirin binihêrin.

Fonksiyona finishMyTask Await bikar tîne da ku li benda encamên operasyonên wekî queryDatabase, sendEmail, logTaskInFile, û yên din bimîne. Ger hûn vê çareseriyê bi ya ku soz lê hatine bikar anîn bidin ber hev, dê wekhevî diyar bibin. Lêbelê, guhertoya Async / Await hemî tevliheviyên syntaktîk pir hêsan dike. Di vê rewşê de, hejmareke mezin a bang û zincîran wekî .then/.catch tune.

Li vir çareseriyek bi derketina jimareyan heye, du vebijark hene.

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

Û li vir çareseriyek bi karanîna fonksiyonên async heye.

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

Pêvajoya çewtiyê

Çewtiyên nehatî girtin di soza redkirî de têne pêçandin. Lêbelê, fonksiyonên async dikarin hewl / girtinê bikar bînin da ku bi hevdemî xeletiyan bikin.

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() fonksiyonek asînkron e ku an bi ser dikeve ("hejmara bêkêmasî") an jî bi xeletiyekê têk diçe ("Bibore, hejmar pir mezin").

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

Ji ber ku mînaka li jor hêvî dike ku canRejectOrReturn were darve kirin, têkçûna wê bixwe dê bibe sedema pêkanîna bloka girtinê. Wekî encamek, fonksiyona foo dê bi nenaskirî (gava ku tiştek di bloka ceribandinê de neyê vegerandin) an jî bi xeletiyek girtinê bi dawî bibe. Wekî encamek, ev fonksiyon dê têk neçe ji ber ku ceribandin / girtin dê fonksiyonê bixwe bi rê ve bibe.

Li vir mînakek din heye:

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

Hêjayî balkişandinê ye ku di nimûneyê de, canRejectOrReturn ji foo tê vegerandin. Foo di vê rewşê de an bi jimareyek bêkêmasî bi dawî dibe an jî Çewtiyek vedigerîne ("Bibore, hejmar pir mezin"). Dê bloka girtinê ti carî neyê darve kirin.

Pirsgirêk ev e ku foo soza ku ji canRejectOrReturn derbas bûye vedigerîne. Ji ber vê yekê çareseriya foo dibe çareseriya canRejectOrReturn. Di vê rewşê de, kod dê tenê ji du rêzan pêk tê:

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

Li vir çi diqewime ger hûn li bendê bi kar bînin û bi hev re vegerin:

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

Di koda li jor de, foo dê hem bi hejmarek bêkêmasî û hem jî bi xeletiyek hatî girtin bi serfirazî derkeve. Li vir wê tu redkirin tune be. Lê foo dê bi canRejectOrReturn vegere, ne bi nenaskirî. Ka em ji vê yekê piştrast bin bi rakirina rêzika vegera li benda canRejectOrReturn():

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

Xeletî û xeletiyên hevpar

Di hin rewşan de, karanîna Async/Await dikare bibe sedema xeletiyan.

Li benda ji bîr kirin

Ev pir caran diqewime - peyva li bendê berî sozê tê ji bîr kirin:

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

Wekî ku hûn dikarin bibînin, di kodê de li bendê an veger tune. Ji ber vê yekê foo her gav bi nenaskirî bêyî 1 saniye dereng derdikeve. Lê soza wê pêk were. Ger ew xeletiyek an redkirinê bavêje, wê hingê UnhandledPromiseRejectionWarning dê were gotin.

Fonksiyonên Async di Callbacks de

Fonksiyonên Asyncê bi gelemperî di .nexşe an .filterê de wekî paşvekêşan têne bikar anîn. Mînak fonksiyona fetchPublicReposCount (navê bikarhêner) ye, ku hejmara depoyên vekirî yên li ser GitHub vedigerîne. Ka em bibêjin sê bikarhênerên ku metrîkên wan hewce ne hene. Li vir koda vê peywirê ye:

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

Pêdiviya me bi hesabên ArfatSalman, octocat, norvig heye. Di vê rewşê de em dikin:

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

Hêja ye ku bala xwe bidin Await di .nexşeyê vegerê de. Li vir jimareyek rêzek soz heye, û .nexşe ji bo her bikarhênerek diyar vegerek nenas e.

Bikaranîna zêde domdar a li bendê

Ka em vê kodê wekî mînakek bigirin:

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

Li vir jimareya repoyê di guhêrbara hejmartinê de tê danîn, dûv re ev hejmar li rêzika hejmartinê tê zêdekirin. Pirsgirêka kodê ev e ku heya ku daneyên bikarhênerê yekem ji serverê neyê, hemî bikarhênerên paşîn dê di moda standby de bin. Ji ber vê yekê, tenê yek bikarhêner di demekê de tête kirin.

Mînakî, heke ji bo pêvajoyek bikarhênerek bi qasî 300 ms hewce dike, wê hingê ji bo hemî bikarhêneran ew jixwe duyemîn e; dema ku bi rengek rêzkirî bi hejmara bikarhêneran ve girêdayî ye. Lê ji ber ku bidestxistina hejmara repoyan bi hev ve ne girêdayî ye, pêvajo dikarin paralel bibin. Ev hewce dike ku bi .map û Promise.all re bixebite:

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

Promise.all komek soz wekî têketinê distîne û sozek vedigerîne. Ya paşîn piştî ku hemî sozên di rêzê de qediyan an di redkirina yekem de temam dibe. Dibe ku ew hemî di heman demê de dest pê nekin - ji bo ku hûn destpêkirina hevdemî piştrast bikin, hûn dikarin p-nexşeyê bikar bînin.

encamê

Fonksiyonên Async ji bo pêşkeftinê her ku diçe girîng dibin. Welê, ji bo karanîna adapteyî ya fonksiyonên async, divê hûn bikar bînin Iterators Async. Pêşdebirek JavaScript divê di vê yekê de baş be.

Skillbox pêşniyar dike:

Source: www.habr.com

Add a comment