اچو ته ڏسو Async/Await جاوا اسڪرپٽ ۾ مثال استعمال ڪندي

مضمون جو ليکڪ جاوا اسڪرپٽ ۾ Async/Await جا مثال جانچي ٿو. مجموعي طور تي، Async/Await هڪ آسان طريقو آهي لکڻ لاءِ هم وقت ساز ڪوڊ. هن مضمون جي ظاهر ٿيڻ کان اڳ، اهڙي ڪوڊ ڪال بڪ ۽ واعدو استعمال ڪندي لکيو ويو هو. اصل مضمون جو ليکڪ مختلف مثالن جو تجزيو ڪندي Async/Await جي فائدن کي ظاهر ڪري ٿو.

اسان توهان کي ياد ڏياريون ٿا: "Habr" جي سڀني پڙهندڙن لاءِ - 10 روبل جي رعايت جڏهن "Habr" پروموشنل ڪوڊ استعمال ڪندي ڪنهن به اسڪل باڪس ڪورس ۾ داخلا.

Skillbox سفارش ڪري ٿو: تعليمي آن لائين ڪورس "جاوا ڊولپر".

callback

ڪال بڪ هڪ فنڪشن آهي جنهن جي ڪال اڻڄاتل دير سان دير ٿي وئي آهي. اڳي، ڪال بڪ ڪوڊ جي انهن علائقن ۾ استعمال ڪيا ويا هئا جتي نتيجو فوري طور تي حاصل نه ٿي سگهيو.

هتي هڪ مثال آهي غير هم وقتي طور تي Node.js ۾ فائل پڙهڻ:

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

مسئلا پيدا ٿين ٿا جڏهن توهان کي هڪ ئي وقت ۾ ڪيترن ئي غير مطابقت واري عملن کي انجام ڏيڻ جي ضرورت آهي. اچو ته هن منظر کي تصور ڪريو: هڪ درخواست ڪئي وئي آهي Arfat صارف ڊيٽابيس کي، توهان کي ان جي profile_img_url فيلڊ پڙهڻ جي ضرورت آهي ۽ someserver.com سرور تان هڪ تصوير ڊائون لوڊ ڪريو.
ڊائون لوڊ ڪرڻ کان پوء، اسان تصوير کي ٻئي شڪل ۾ تبديل ڪندا آهيون، مثال طور PNG کان JPEG تائين. جيڪڏهن تبديلي ڪامياب ٿي وئي، هڪ خط موڪليو ويو آهي صارف جي اي ميل ڏانهن. اڳيون، واقعي جي باري ۾ معلومات داخل ڪئي وئي آهي transformations.log فائل ۾، تاريخ کي اشارو ڪندي.

اهو ڌيان ڏيڻ جي قابل آهي ڪال بڪ جي اوورليپ ۽ } جي وڏي تعداد) ڪوڊ جي آخري حصي ۾. ان کي ڪال بيڪ هيل يا پيراميڊ آف ڊوم سڏيو ويندو آهي.

هن طريقي جا نقصان واضح آهن:

  • هي ڪوڊ پڙهڻ ڏکيو آهي.
  • اهو پڻ ڏکيو آهي ته غلطين کي سنڀالڻ، جيڪو اڪثر ڪري غريب ڪوڊ معيار جي ڪري ٿو.

هن مسئلي کي حل ڪرڻ لاء، واعدو جاوا اسڪرپٽ ۾ شامل ڪيو ويو. اهي توهان کي لفظ سان ڪال بيڪ جي deep nesting کي تبديل ڪرڻ جي اجازت ڏين ٿا. پوء.

واعدن جو مثبت پاسو اهو آهي ته اهي ڪوڊ کي گهڻو بهتر پڙهڻ لائق بڻائين ٿا، کاٻي کان ساڄي بدران مٿي کان هيٺ تائين. بهرحال، واعدو پڻ انهن جا مسئلا آهن:

  • توهان کي تمام گهڻو شامل ڪرڻ جي ضرورت آهي. پوء.
  • ڪوشش/پڪڙڻ جي بدران، .catch استعمال ڪيو ويندو آهي سڀني غلطين کي سنڀالڻ لاءِ.
  • ھڪڙي لوپ اندر گھڻن واعدن سان ڪم ڪرڻ ھميشه آسان نه آھي؛ ڪجھ ڪيسن ۾، اھي ڪوڊ کي پيچيده ڪن ٿا.

هتي هڪ مسئلو آهي جيڪو آخري نقطي جي معني کي ظاهر ڪندو.

فرض ڪريو اسان وٽ ھڪڙو لوپ آھي جيڪو 0 کان 10 تائين انگن جي ھڪڙي ترتيب کي بي ترتيب وقفي (0-n سيڪنڊن) تي پرنٽ ڪري ٿو. واعدو استعمال ڪندي، توهان کي هن لوپ کي تبديل ڪرڻ جي ضرورت آهي ته جيئن انگ 0 کان 10 تائين ترتيب سان ڇپيا وڃن. تنهن ڪري، جيڪڏهن صفر کي پرنٽ ڪرڻ ۾ 6 سيڪنڊن ۽ هڪ کي پرنٽ ڪرڻ ۾ 2 سيڪنڊن جو وقت لڳندو آهي، صفر کي پهرين پرنٽ ڪرڻ گهرجي، ۽ پوء هڪ ڇپجڻ لاءِ ڳڻپ شروع ٿيندي.

۽ يقينا، اسان استعمال نٿا ڪريون Async/Await يا .sort هن مسئلي کي حل ڪرڻ لاءِ. ھڪڙو مثال حل آھي آخر ۾.

Async افعال

ES2017 (ES8) ۾ async افعال جو اضافو وعدن سان ڪم ڪرڻ جي ڪم کي آسان بڻائي ٿو. مان نوٽ ڪريان ٿو ته async افعال ڪم ڪن ٿا "مٿين" واعدن جي. اهي افعال معيار جي لحاظ کان مختلف تصورن جي نمائندگي نٿا ڪن. Async افعال جو مقصد ڪوڊ جي متبادل طور آھي جيڪو واعدو استعمال ڪري ٿو.

Async/Await اهو ممڪن بڻائي ٿو ڪم کي ترتيب ڏيڻ غير هم وقتي ڪوڊ سان هم وقت ساز انداز ۾.

اهڙيء طرح، واعدو ڄاڻڻ آسان بڻائي ٿو Async/Await جي اصولن کي سمجهڻ.

نحو

عام طور تي اهو ٻن لفظن تي مشتمل هوندو آهي: async ۽ await. پهريون لفظ فعل کي هم وقت سازي ۾ بدلائي ٿو. اهڙا افعال انتظار جي استعمال جي اجازت ڏين ٿا. ڪنهن ٻئي صورت ۾، هن فنڪشن کي استعمال ڪندي هڪ غلطي پيدا ڪندو.

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

NB! اهو ياد رکڻ جي قابل آهي ته طبقاتي تعمير ڪندڙ ۽ حاصل ڪندڙ / سيٽرز غير هم وقت نٿا ٿي سگهن.

اصطلاحات ۽ عمل جي ضابطن

Async فنڪشن بنيادي طور تي معياري JS افعال سان ملندڙ جلندڙ آهن، پر استثنا آهن.

اهڙيء طرح، async افعال هميشه واعدو موٽائي ٿو:

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

خاص طور تي، fn واپسي string هيلو. خير، ڇاڪاڻ ته هي هڪ هم وقت ساز فنڪشن آهي، اسٽرنگ جي قيمت هڪ واعدو ۾ لپي وئي آهي هڪ تعمير ڪندڙ استعمال ڪندي.

هتي 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 فنڪشن هميشه هڪ واعدو ڪڍي ٿو، قطع نظر جيڪو واپس ڪيو ويو آهي.

Asynchronous فنڪشن هر انتظار تي روڪي ٿو.

انتظار اظهار کي متاثر ڪري ٿو. تنهن ڪري، جيڪڏهن اظهار هڪ واعدو آهي، 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)؛.
  • Await استعمال ڪرڻ کان پوء، فنڪشن جي عمل کي معطل ڪيو ويو آهي جيستائين ان جي قيمت حاصل نه ٿئي (موجوده صورتحال ۾ اهو 9 آهي).
  • delayAndGetRandom (1000) fn فنڪشن جي عمل کي روڪي ٿو جيستائين اهو پاڻ کي مڪمل نه ڪري (1 سيڪنڊ کان پوء). اهو مؤثر طور تي fn فنڪشن کي 1 سيڪنڊن لاءِ روڪي ٿو.
  • delayAndGetRandom(1000) حل ذريعي هڪ بي ترتيب قدر واپس ڪري ٿو، جيڪو پوء متغير ب کي لڳايو ويو آهي.
  • خير، variable c سان معاملو متغير الف سان گڏ ساڳيو آهي. ان کان پوء، هر شيء هڪ سيڪنڊ لاء روڪي ٿي، پر هاڻي delayAndGetRandom(1000) ڪجھ به نه ٿو ڏئي ڇاڪاڻ ته اهو گهربل ناهي.
  • نتيجي طور، قدر ڳڻيا ويندا آھن فارمولا a + b * c استعمال ڪندي. نتيجو هڪ واعدو ۾ لپي ويو آهي Promise.resolve استعمال ڪندي ۽ فنڪشن طرفان واپس آيو.

اهي رڪاوٽون ES6 ۾ جنريٽر جي ياد ڏياري سگھن ٿيون، پر ان ۾ ڪجهه آهي توهان جا سبب.

مسئلو حل ڪرڻ

خير، هاڻي اچو ته مٿي ذڪر ڪيل مسئلي جو حل ڏسو.

FinishMyTask فنڪشن Await استعمال ڪري ٿو عملن جي نتيجن جو انتظار ڪرڻ لاءِ جيئن queryDatabase، sendEmail، 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 ختم ٿي ويندو يا ته اڻ ڄاڻايل (جڏهن ڪوشش جي بلاڪ ۾ ڪجھ به نه موٽايو ويندو آهي) يا هڪ غلطي سان پڪڙي ويندي آهي. نتيجي طور، هي فنڪشن ناڪام نه ٿيندو ڇو ته ڪوشش / پڪڙي فنڪشن کي پاڻ سنڀاليندو foo.

هتي هڪ ٻيو مثال آهي:

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

اهو حقيقت تي ڌيان ڏيڻ جي قابل آهي ته مثال طور، canRejectOrReturn foo کان واپسي آهي. 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(username) فنڪشن، جيڪو 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'];
}

اسان کي ArfatSalman، octocat، norvig اڪائونٽس جي ضرورت آهي. هن معاملي ۾ اسين ڪندا آهيون:

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

اهو .map ڪال بڪ ۾ انتظار ڪرڻ تي ڌيان ڏيڻ جي قابل آهي. هتي ڳڻپ وعدن جو هڪ صف آهي، ۽ .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 Iterators. هڪ جاوا اسڪرپٽ ڊولپر کي هن ۾ چڱي طرح واقف هجڻ گهرجي.

Skillbox سفارش ڪري ٿو:

جو ذريعو: www.habr.com

تبصرو شامل ڪريو