راځئ چې د مثالونو په کارولو سره په جاواسکریپټ کې Async/Await وګورو

د مقالې لیکوال په جاواسکریپټ کې د Async/Await مثالونه معاینه کوي. په ټولیز ډول، Async/Await د غیر متناسب کوډ لیکلو لپاره یوه اسانه لار ده. مخکې له دې چې دا فیچر ښکاره شي، دا ډول کوډ د کال بیکونو او ژمنو په کارولو سره لیکل شوی و. د اصلي مقالې لیکوال د مختلف مثالونو تحلیل کولو سره د Async/Await ګټې څرګندوي.

موږ یادونه کوو: د ټولو هابر لوستونکو لپاره - د 10 روبل تخفیف کله چې د هابر پرومو کوډ په کارولو سره د مهارت بکس کوم کورس کې نوم لیکنه وکړئ.

Skillbox وړاندیز کوي: تعلیمي آنلاین کورس "جاوا پرمخ وړونکی".

callback

کال بیک یو فنکشن دی چې زنګ یې د نامعلوم وخت لپاره ځنډول کیږي. پخوا، د کوډ په هغو ساحو کې کال بیکونه کارول کیده چیرې چې پایله یې سمدلاسه نشي ترلاسه کیدی.

دلته په Node.js کې د غیر متناسب فایل لوستلو مثال دی:

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

ستونزې رامینځته کیږي کله چې تاسو اړتیا لرئ په یوځل کې څو غیر متمرکز عملیات ترسره کړئ. راځئ چې دا سناریو تصور کړو: د عرفات کارونکي ډیټابیس ته غوښتنه شوې ، تاسو اړتیا لرئ د دې پروفایل_img_url ساحه ولولئ او د someserver.com سرور څخه عکس ډاونلوډ کړئ.
د ډاونلوډ کولو وروسته، موږ عکس په بل شکل بدلوو، د بیلګې په توګه د PNG څخه JPEG ته. که تبادله بریالۍ وه، یو لیک د کارونکي بریښنالیک ته لیږل کیږي. بیا، د پیښې په اړه معلومات د transformations.log فایل کې داخل شوي، نیټه په ګوته کوي.

دا د کوډ په وروستۍ برخه کې د کال بیکس او د } لوی شمیر) ته پام کولو ارزښت لري. دې ته د کال بیک دوزخ یا د عذاب اهرام ویل کیږي.

د دې میتود نیمګړتیاوې څرګندې دي:

  • دا کوډ لوستل ستونزمن دي.
  • د غلطیو اداره کول هم ستونزمن دي، کوم چې ډیری وختونه د ضعیف کوډ کیفیت المل کیږي.

د دې ستونزې د حل لپاره، ژمنې جاواسکریپټ کې اضافه شوې. دوی تاسو ته اجازه درکوي چې د کلمې سره د کال بیکس ژور ځړول بدل کړئ.

د ژمنو مثبت اړخ دا دی چې دوی کوډ خورا ښه د لوستلو وړ کوي، له پورته څخه ښکته نه د کیڼ څخه ښیې ته. په هرصورت، ژمنې هم خپلې ستونزې لري:

  • تاسو اړتیا لرئ چې ډیری اضافه کړئ.
  • د هڅه/کیچ پر ځای، .catch د ټولو غلطیو اداره کولو لپاره کارول کیږي.
  • په یوه لوپ کې د ډیری ژمنو سره کار کول تل اسانه ندي؛ په ځینو مواردو کې، دوی کوډ پیچلی کوي.

دلته یوه ستونزه ده چې د وروستي ټکي معنی به ښکاره کړي.

فرض کړئ چې موږ د لوپ لپاره لرو چې د 0 څخه تر 10 پورې په تصادفي وقفو (0-n ثانیو) کې د شمیرو ترتیب چاپوي. د ژمنو په کارولو سره، تاسو اړتیا لرئ چې دا لوپ بدل کړئ ترڅو شمیرې له 0 څخه تر 10 پورې په ترتیب سره چاپ شي. نو، که چیرې د صفر چاپولو لپاره 6 ثانیې او د یو چاپ کولو لپاره 2 ثانیې وخت ونیسي، صفر باید لومړی چاپ شي، او بیا د چاپ لپاره به بیا شمیرنه پیل شي.

او البته، موږ د دې ستونزې د حل لپاره Async/Await یا .sort نه کاروو. یو مثال حل په پای کې دی.

د Async دندې

په ES2017 (ES8) کې د async دندو اضافه کول د ژمنو سره د کار کولو دنده ساده کړې. زه یادونه کوم چې د async افعال د ژمنو "پورته" کار کوي. دا دندې د کیفیت له پلوه مختلف مفکورې نه استازیتوب کوي. د Async افعال د کوډ لپاره د بدیل په توګه ټاکل شوي چې ژمنې کاروي.

Async/Await دا ممکنه کوي چې کار په همغږي سټایل کې د غیر متناسب کوډ سره تنظیم کړئ.

په دې توګه، د ژمنو پوهیدل د Async/Await په اصولو پوهیدل اسانه کوي.

العروض

عموما دا دوه کلیدي کلمې لري: async او انتظار. لومړۍ کلمه فنکشن په اسینکرونس بدلوي. دا ډول دندې د انتظار کارولو ته اجازه ورکوي. په بل هر حالت کې، د دې فنکشن کارول به یوه تېروتنه رامنځته کړي.

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

Async د فنکشن اعلامیې په پیل کې دننه کیږي، او د تیر فنکشن په صورت کې، د "=" نښه او قوس ترمنځ.

دا افعال په یو څیز کې د میتودونو په توګه کیښودل کیدی شي یا په ټولګي اعلامیه کې کارول کیدی شي.

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

نو! دا د یادولو وړ ده چې د ټولګي جوړونکي او ګیټرز/سیټرونه غیر متناسب نه وي.

سیمانټیک او د اجرا کولو قواعد

د Async افعال اساسا د معیاري JS افعال سره ورته دي، مګر استثناوې شتون لري.

په دې توګه، د async افعال تل ژمنې بیرته راګرځوي:

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

په ځانګړې توګه، fn د تار هیلو بیرته راګرځوي. ښه، ځکه چې دا یو غیر متناسب فعالیت دی، د تار ارزښت د جوړونکي په کارولو سره په ژمنه کې پوښل شوی.

دلته د Async پرته یو بدیل ډیزاین دی:

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

په دې حالت کې، ژمنه بیرته "په لاسي توګه" راځي. یو غیر متناسب فعالیت تل په نوې ژمنې پوښل کیږي.

که د بیرته ستنیدو ارزښت یو ابتدايي وي، د async فنکشن په یوه ژمنه کې د پوښلو سره ارزښت بیرته راګرځوي. که د بیرته ستنیدو ارزښت د ژمنې اعتراض وي، د هغې حل په نوې ژمنه کې بیرته راستانه کیږي.

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

مګر څه پیښیږي که چیرې د غیر متناسب فعالیت دننه کومه تېروتنه شتون ولري؟

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

که دا پروسس نشي، foo() به د رد سره ژمنه بیرته راولي. په دې حالت کې، Promise.reject چې یوه تېروتنه لري د Promise.resolve پرځای بیرته راستانه شي.

د Async دندې تل یوه ژمنه تولیدوي، پرته له دې چې بیرته راستانه شي.

غیر متناسب افعال په هر انتظار کې ودریږي.

انتظار څرګندونه اغیزه کوي. نو، که بیان یوه ژمنه وي، د async فعالیت تر هغه پورې ځنډول کیږي تر څو چې ژمنه پوره نشي. که بیان ژمنه نه وي، دا د Promise.resolve له لارې په ژمنې بدلیږي او بیا بشپړیږي.

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

او دلته د fn فنکشن څنګه کار کوي تشریح دی.

  • د زنګ وهلو وروسته، لومړۍ کرښه له const a = await 9 څخه بدلیږي. in const a = انتظار وکړئ Promise.resolve(9);
  • د انتظار کارولو وروسته، د فعالیت اجرا کول تر هغه پورې ځنډول کیږي تر هغه چې یو خپل ارزښت ترلاسه کړي (په اوسني حالت کې دا 9 دی).
  • delayAndGetRandom(1000) د fn فنکشن اجرا کول ودروي تر هغه چې دا پخپله بشپړ نشي (د 1 ثانیې وروسته). دا په مؤثره توګه د 1 ثانیو لپاره د fn فعالیت ودروي.
  • delayAndGetRandom(1000) د حل له لارې یو تصادفي ارزښت راګرځوي، کوم چې بیا متغیر b ته ټاکل کیږي.
  • ښه، د متغیر c سره قضیه د متغیر الف سره قضیه سره ورته ده. له هغې وروسته ، هرڅه د یوې ثانیې لپاره ودریږي ، مګر اوس ځنډول او ګیټ رینډم (1000) هیڅ شی نه راګرځوي ځکه چې دا اړین ندي.
  • د پایلې په توګه، ارزښتونه د فورمول a + b * c په کارولو سره محاسبه کیږي. پایله د Promise.resolve په کارولو سره په ژمنه کې پوښل شوې او د فنکشن لخوا بیرته راستانه شوې.

دا وقفې ممکن په ES6 کې د جنراتورونو یادونه وکړي ، مګر پدې کې یو څه شتون لري ستاسو دلایل.

د ستونزې حل کول

ښه، اوس راځئ چې د پورته ذکر شوي ستونزې حل وګورو.

د FinishMyTask فنکشن د عملیاتو پایلو ته انتظار کولو لپاره انتظار کاروي لکه د پوښتنې ډیټابیس ، بریښنالیک لیږل ، logTaskInFile ، او نور. که تاسو دا حل له هغه سره پرتله کړئ چیرې چې ژمنې کارول شوې وې ، ورته والی به څرګند شي. په هرصورت، د Async/Await نسخه ټول ترکیبي پیچلتیاوې خورا ساده کوي. په دې حالت کې، د کال بیکونو او زنځیرونو لوی شمیر شتون نلري لکه .then/.catch.

دلته د شمیرو د محصول سره حل دی، دوه اختیارونه شتون لري.

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

او دلته د async افعال په کارولو سره حل دی.

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

د پروسس کولو تېروتنه

نه منل شوې تېروتنې په رد شوې ژمنې کې پوښل شوي. په هرصورت، د async افعال کولی شي په همغږي ډول د غلطیو اداره کولو لپاره هڅه/کیچ وکاروي.

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() یو غیر متناسب فنکشن دی چې یا بریالی کیږي ("بشپړ شمیر") یا د غلطۍ سره ناکام کیږي ("بخښنه، شمیره ډیره لویه").

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

څرنګه چې پورتنۍ بیلګه د canRejectOrReturn د اجرا کولو تمه لري، د هغې خپله ناکامي به د کیچ بلاک اجرا کیدو پایله ولري. د پایلې په توګه، د foo فنکشن به د نه تعریف شوي (کله چې د هڅه کولو بلاک کې هیڅ شی بیرته نه راځي) یا د یوې تېروتنې سره پای ته ورسیږي. د پایلې په توګه، دا فنکشن به ناکام نشي ځکه چې هڅه/کیچ به پخپله فنکشن اداره کړي.

دلته یو بل مثال دی:

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

دا د دې حقیقت په پام کې نیولو سره ارزښت لري چې په مثال کې، canRejectOrReturn د foo څخه راستانه شوی. فو په دې حالت کې یا د بشپړ شمیرې سره پای ته رسیږي یا یوه تېروتنه راګرځوي ("بخښنه، شمیره ډیره لویه"). د کیچ بلاک به هیڅکله اعدام نشي.

ستونزه دا ده چې foo هغه ژمنه بیرته راګرځوي چې له canRejectOrReturn څخه تیریږي. نو د foo حل د canRejectOrReturn لپاره حل کیږي. په دې حالت کې، کوډ به یوازې دوه کرښې ولري:

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

دلته څه پیښیږي که تاسو انتظار وکاروئ او یوځای بیرته راشئ:

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

په پورتني کوډ کې، foo به د کامل شمیرې او د یوې غلطۍ نیول شوي دواړه سره په بریالیتوب سره وځي. دلته به هیڅ انکار نه وي. مګر foo به د canRejectOrReturn سره بیرته راستانه شي، نه د نامعلوم سره. راځئ چې د بیرته ستنیدو انتظار په لرې کولو سره دا ډاډ ترلاسه کړو canRejectOrReturn() لاین:

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

عام غلطۍ او نیمګړتیاوې

په ځینو مواردو کې، د Async/Await کارول کولی شي د غلطیو لامل شي.

هېر شوی انتظار

دا ډیری وختونه پیښیږي - د انتظار کلیدي د ژمنې دمخه هیر شوی:

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

لکه څنګه چې تاسو لیدلی شئ، په کوډ کې هیڅ انتظار یا بیرته ستنیدل شتون نلري. له همدې امله foo تل د 1 ثانیې ځنډ پرته د نه تعریف شوي سره وځي. خو وعده به پوره کوي. که دا کومه تېروتنه یا رد کړي، نو د UnhandledPromiseRejectionWarning به ویل کیږي.

په کال بیکس کې د Async دندې

د Async افعال ډیری وختونه په .map یا .filter کې د کال بیک په توګه کارول کیږي. یوه بیلګه د fetchPublicReposCount (کارن نوم) فنکشن دی، کوم چې په GitHub کې د خلاص ذخیره کولو شمیر بیرته راګرځوي. راځئ چې ووایو دلته درې کاروونکي شتون لري چې میټریکونه یې موږ ته اړتیا لرو. دلته د دې کار لپاره کوډ دی:

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

موږ د عرفات سلمان، octocat، norvig حسابونو ته اړتیا لرو. پدې حالت کې موږ دا کوو:

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

دا د پام وړ دی چې په .map کال بیک کې انتظار ته پام وکړئ. دلته شمیرنه د ژمنو لړۍ ده، او نقشه د هر مشخص کارونکي لپاره یو نامعلوم کال بیک دی.

د انتظار ډیر دوامداره استعمال

راځئ چې دا کوډ د مثال په توګه واخلو:

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

دلته د ریپو شمیره د شمیرنې متغیر کې ځای په ځای شوې ، بیا دا شمیره د شمیرې سرې ته اضافه کیږي. د کوډ سره ستونزه دا ده چې تر هغه چې د لومړي کارونکي ډیټا له سرور څخه راشي ، ټول راتلونکي کارونکي به په سټنډرډ حالت کې وي. په دې توګه، یوازې یو کاروونکي په یو وخت کې پروسس کیږي.

که، د مثال په توګه، دا د یو کاروونکي پروسس کولو لپاره شاوخوا 300 ms وخت نیسي، نو د ټولو کاروونکو لپاره دا لا دمخه یوه ثانیه ده؛ په کرښه کې مصرف شوي وخت د کاروونکو په شمیر پورې اړه لري. مګر څنګه چې د ریپو شمیره ترلاسه کول په یو بل پورې اړه نلري ، پروسې موازي کیدی شي. دا د .map او 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 د ان پټ په توګه د ژمنو لړۍ ترلاسه کوي او ژمنه بیرته ورکوي. وروستنۍ، وروسته له دې چې په صف کې ټولې ژمنې بشپړې شوې یا په لومړي رد کې بشپړې شوې. دا ممکن پیښ شي چې دوی ټول په ورته وخت کې پیل نه کوي - د یوځل پیل کولو ډاډ ترلاسه کولو لپاره ، تاسو کولی شئ د p-map وکاروئ.

پایلې

د Async فعالیتونه د پراختیا لپاره په زیاتیدونکي توګه مهم کیږي. ښه، د async دندو د تطبیق وړ کارولو لپاره دا د کارولو ارزښت لري Async تکرارونکي. د جاواسکریپټ پراختیا کونکی باید پدې کې ښه پوه وي.

Skillbox وړاندیز کوي:

سرچینه: www.habr.com

Add a comment