Hayu urang tingali Async / Await dina JavaScript nganggo conto

Panulis artikel nalungtik conto Async / Await dina JavaScript. Gemblengna, Async/Await mangrupikeun cara anu merenah pikeun nyerat kodeu asinkron. Sateuacan fitur ieu muncul, kode sapertos ieu ditulis nganggo callback sareng janji. Panulis artikel asli ngungkabkeun kaunggulan Async/Await ku nganalisa sababaraha conto.

Kami ngingetan: pikeun sakabéh pamiarsa "Habr" - diskon 10 rubles nalika enrolling dina sagala Tangtu Skillbox ngagunakeun "Habr" kode promosi.

Skillbox nyarankeun: Kursus online atikan "Pamekar Java".

callback

Callback mangrupikeun fungsi anu teleponna ditunda salamina. Saméméhna, callbacks dipaké dina eta wewengkon kode mana hasilna teu bisa langsung diala.

Ieu conto asynchronously maca file di Node.js:

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

Masalah timbul nalika anjeun kedah ngalakukeun sababaraha operasi asynchronous sakaligus. Hayu urang ngabayangkeun skenario kieu: pamundut dijieun ka database pamaké Arfat, anjeun kudu maca widang profile_img_url na tur ngundeur gambar ti server someserver.com.
Saatos diunduh, urang ngarobih gambar kana format anu sanés, contona tina PNG ka JPEG. Upami konvérsi suksés, surat dikirim ka email pangguna. Salajengna, inpormasi ngeunaan acara diasupkeun kana file transformations.log, nunjukkeun tanggalna.

Eta sia nengetan tumpang tindihna callbacks sarta jumlah badag }) dina bagian ahir kode. Disebutna Callback Hell atanapi Pyramid of Doom.

Karugian tina metoda ieu atra:

  • Kode ieu hésé dibaca.
  • Éta ogé hésé pikeun nanganan kasalahan, anu sering nyababkeun kualitas kode anu goréng.

Pikeun ngajawab masalah ieu, janji ditambahkeun kana JavaScript. Aranjeunna ngidinan Anjeun pikeun ngaganti nyarang jero tina callbacks ku kecap .lajeng.

Aspék positip tina janji nyaéta aranjeunna ngajantenkeun kode langkung saé dibaca, ti luhur ka handap tinimbang ti kénca ka katuhu. Nanging, janji ogé ngagaduhan masalahna:

  • Anjeun kudu nambahan loba .lajeng.
  • Gantina coba / nyekel, .catch dipaké pikeun nanganan sagala kasalahan.
  • Gawe sareng sababaraha janji dina hiji loop henteu salawasna merenah; dina sababaraha kasus, aranjeunna ngahesekeun kode.

Ieu mangrupikeun masalah anu bakal nunjukkeun harti titik anu terakhir.

Anggap urang boga loop for nu nyitak runtuyan angka ti 0 nepi ka 10 dina interval acak (0–n detik). Nganggo janji, anjeun kedah ngarobih loop ieu supados angka-angka dicitak dina sekuen ti 0 dugi ka 10. Janten, upami peryogi 6 detik pikeun nyitak enol sareng 2 detik pikeun nyitak hiji, enol kedah dicitak heula, teras teras. undur mundur pikeun nyitak hiji bakal dimimitian.

Sarta tangtu, urang teu make Async / Await atanapi .sort pikeun ngajawab masalah ieu. Hiji conto solusi aya dina tungtungna.

fungsi Async

Penambahan fungsi async dina ES2017 (ES8) nyederhanakeun tugas damel sareng janji. Kuring dicatet yén fungsi async dianggo "di luhureun" janji. Fungsi ieu henteu ngagambarkeun konsép anu béda sacara kualitatif. Fungsi Async dimaksudkeun salaku alternatif pikeun kode anu nganggo janji.

Async / Await ngamungkinkeun pikeun ngatur karya sareng kode Asynchronous dina gaya sinkron.

Ku kituna, terang jangji ngagampangkeun ngartos prinsip Async / Await.

sintaksis

Biasana éta diwangun ku dua kecap konci: async sareng ngantosan. Kecap kahiji ngarobah fungsi kana asynchronous. fungsi sapertos ngidinan pamakéan await. Dina kasus anu sanés, nganggo fungsi ieu bakal ngahasilkeun kasalahan.

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

Async diselapkeun dina awal deklarasi fungsi, sareng dina kasus fungsi panah, antara tanda "="" sareng tanda kurung.

Fungsi ieu bisa ditempatkeun dina obyék salaku métode atawa dipaké dina deklarasi kelas.

// 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! Eta sia remembering yén konstruktor kelas na getters / setters teu tiasa Asynchronous.

Semantik jeung aturan palaksanaan

Fungsi Async dasarna sami sareng fungsi JS standar, tapi aya pengecualian.

Ku kituna, fungsi async salawasna balik jangji:

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

Husus, fn mulih string halo. Muhun, saprak ieu mangrupa fungsi Asynchronous, nilai string dibungkus dina jangji ngagunakeun constructor a.

Ieu desain alternatif tanpa Async:

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

Dina hal ieu, jangji dipulangkeun "sacara manual". Fungsi asynchronous sok dibungkus ku jangji anyar.

Lamun nilai balik téh primitif, fungsi async balik nilai ku wrapping eta dina jangji. Upami nilai balikna mangrupikeun obyék jangji, résolusina dipulangkeun dina jangji énggal.

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

Tapi naon anu lumangsung lamun aya kasalahan dina hiji fungsi Asynchronous?

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

Lamun teu diprosés, foo () bakal balik jangji jeung tampikan. Dina kaayaan ieu, Promise.reject ngandung kasalahan bakal balik tinimbang Promise.resolve.

Fungsi Async salawasna ngahasilkeun jangji, paduli naon anu dipulangkeun.

Fungsi Asynchronous ngareureuhkeun unggal ngantosan.

Ngadagoan mangaruhan ekspresi. Janten, upami éksprési mangrupikeun jangji, fungsi async ditunda dugi ka jangji kaeusi. Lamun babasan teu jangji, eta dirobah jadi jangji via Promise.resolve lajeng réngsé.

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

Sareng ieu mangrupikeun pedaran kumaha fungsi fn jalan.

  • Saatos nelepon eta, baris kahiji dirobah tina const a = await 9; dina const a = ngantosan Promise.resolve (9);.
  • Sanggeus ngagunakeun Await, palaksanaan fungsi ditunda dugi a meunang nilai na (dina kaayaan ayeuna éta 9).
  • delayAndGetRandom(1000) ngareureuhkeun palaksanaan fungsi fn nepi ka rengse sorangan (sanggeus 1 detik). Ieu sacara efektif ngeureunkeun pungsi fn salami 1 detik.
  • delayAndGetRandom (1000) via ngabéréskeun mulih hiji nilai acak, nu lajeng ditugaskeun ka variabel b.
  • Nya, kasus variabel c sami sareng kasus variabel a. Sanggeus éta, sagalana eureun sadetik, tapi ayeuna delayAndGetRandom(1000) mulih nanaon sabab teu diperlukeun.
  • Hasilna, nilai diitung nganggo rumus a + b * c. Hasilna dibungkus ku jangji nganggo Promise.resolve sareng dipulangkeun ku fungsina.

Reureuh ieu tiasa ngingetkeun generator di ES6, tapi aya waé alesan anjeun.

Ngarengsekeun masalah

Nya, ayeuna hayu urang tingali solusi pikeun masalah anu disebatkeun di luhur.

Fungsi finishMyTask ngagunakeun Await pikeun ngadagoan hasil operasi saperti queryDatabase, sendEmail, logTaskInFile, jeung sajabana. Upami anjeun ngabandingkeun solusi ieu sareng anu mana janji dianggo, kamiripan bakal atra. Sanajan kitu, versi Async / Await greatly simplifies sagala complexities sintaksis. Dina hal ieu, euweuh angka nu gede ngarupakeun callbacks na ranté kawas .then/.catch.

Di dieu nyaeta solusi kalawan kaluaran angka, aya dua pilihan.

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

Sareng ieu mangrupikeun solusi anu nganggo fungsi async.

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

Kasalahan ngolah

Kasalahan anu teu kacekel dibungkus ku jangji anu ditolak. Tapi, fungsi async tiasa nganggo try/catch pikeun nanganan kasalahan sacara sinkron.

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 () mangrupa fungsi Asynchronous nu boh suksés ("Jumlah sampurna") atawa gagal kalawan kasalahan ("Hapunten, jumlah badag teuing").

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

Kusabab conto di luhur nyangka canRejectOrReturn ngaéksekusi, gagalna sorangan bakal ngahasilkeun palaksanaan blok nyekel. Hasilna, fungsi foo bakal ditungtungan ku boh undefined (lamun nanaon geus balik di blok try) atanapi ku kasalahan bray. Hasilna, fungsi ieu moal gagal sabab try / nyekel bakal nanganan fungsi foo sorangan.

Ieu conto sejen:

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

Éta patut nengetan kanyataan yén dina conto, canRejectOrReturn dipulangkeun ti foo. Foo dina hal ieu boh terminates kalawan jumlah sampurna atawa balik hiji Kasalahan ("Hapunten, angka badag teuing"). Blok nyekel moal pernah dieksekusi.

Masalahna nyaeta foo mulih jangji diliwatan ti canRejectOrReturn. Janten solusi pikeun foo janten solusi pikeun canRejectOrReturn. Dina hal ieu, kode bakal diwangun ku ukur dua baris:

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

Ieu naon anu kajantenan upami anjeun nganggo await sareng uih deui babarengan:

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

Dina kode di luhur, foo bakal kaluar suksés kalayan duanana jumlah sampurna jeung kasalahan bray. Moal aya panolakan di dieu. Tapi foo bakal balik kalawan canRejectOrReturn, teu kalawan undefined. Hayu urang pastikeun ieu ku nyoplokkeun balik await canRejectOrReturn() garis:

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

Kasalahan umum sareng pitfalls

Dina sababaraha kasus, nganggo Async/Await tiasa nyababkeun kasalahan.

Poho nungguan

Ieu sering kajantenan - kecap konci anu ditungguan hilap sateuacan janji:

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

Sakumaha anjeun tiasa tingali, teu aya anu ngantosan atanapi uih deui dina kodeu. Kituna foo salawasna kaluar kalawan undefined tanpa reureuh 1 detik. Tapi jangji bakal dicumponan. Upami éta ngalungkeun kasalahan atanapi panolakan, maka UnhandledPromiseRejectionWarning bakal disebat.

Fungsi Async dina Callbacks

fungsi Async rada mindeng dipaké dina .map atanapi .filter sakumaha callbacks. Hiji conto nyaéta fungsi fetchPublicReposCount(ngaran pamaké), nu mulihkeun jumlah repositories kabuka dina GitHub. Sebutkeun aya tilu pangguna anu métrikna urang peryogikeun. Ieu kodeu pikeun tugas ieu:

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

Urang peryogi akun ArfatSalman, octocat, norvig. Dina hal ieu urang ngalakukeun:

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

Éta patut nengetan Await dina .map callback. Di dieu diitung mangrupa Asép Sunandar Sunarya janji, sarta .peta mangrupa callback anonim pikeun tiap pamaké dieusian.

Overly konsisten pamakéan await

Hayu urang nyandak kode ieu salaku conto:

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

Di dieu jumlah repo disimpen dina variabel count, teras nomer ieu ditambahkeun kana Asép Sunandar Sunarya cacah. Masalah sareng kode nyaéta dugi ka data pangguna munggaran sumping ti server, sadaya pangguna salajengna bakal aya dina modeu sayaga. Ku kituna, ngan hiji pamaké diolah dina hiji waktu.

Upami, contona, peryogi kirang langkung 300 mdet pikeun ngolah hiji pangguna, maka pikeun sadaya pangguna éta sadetik; waktos anu dianggo sacara linier gumantung kana jumlah pangguna. Tapi saprak meunangkeun jumlah repo teu gumantung ka silih, prosés bisa parallelized. Ieu merlukeun gawé bareng .map na 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 nampi sajumlah janji salaku input sareng ngabalikeun jangji. Anu terakhir, saatos sadaya janji dina susunan parantos réngsé atanapi dina panolakan anu munggaran, parantos réngsé. Ieu bisa lumangsung yén maranéhna sagala teu ngamimitian dina waktos anu sareng - guna mastikeun mimiti simultaneous, anjeun tiasa nganggo p-peta.

kacindekan

Fungsi Async janten langkung penting pikeun pangwangunan. Nya, pikeun panggunaan adaptif fungsi async, anjeun kedah nganggo Async Iterators. A pamekar JavaScript kudu well versed dina ieu.

Skillbox nyarankeun:

sumber: www.habr.com

Tambahkeun komentar