অ্যাসিঙ্ক ফাংশন: প্রতিশ্রুতি বন্ধুত্বপূর্ণ করা

অ্যাসিঙ্ক ফাংশনগুলি আপনাকে প্রতিশ্রুতি-ভিত্তিক কোড লিখতে দেয় যেন এটি সিঙ্ক্রোনাস।

Async ফাংশনগুলি Chrome, Edge, Firefox এবং Safari-এ ডিফল্টরূপে সক্রিয় করা হয় এবং সেগুলি বেশ স্পষ্টতই অসাধারণ। তারা আপনাকে প্রতিশ্রুতি-ভিত্তিক কোড লেখার অনুমতি দেয় যেন এটি সিঙ্ক্রোনাস, কিন্তু মূল থ্রেড ব্লক না করে। তারা আপনার অ্যাসিঙ্ক্রোনাস কোড কম "চতুর" এবং আরও পাঠযোগ্য করে তোলে।

Async ফাংশন এই মত কাজ করে:

async function myFirstAsyncFunction() {
  try {
    const fulfilledValue = await promise;
  } catch (rejectedValue) {
    // …
  }
}

আপনি যদি একটি ফাংশন সংজ্ঞার আগে async কীওয়ার্ড ব্যবহার করেন, তাহলে আপনি ফাংশনের মধ্যে await ব্যবহার করতে পারেন। আপনি যখন একটি প্রতিশ্রুতির await , তখন প্রতিশ্রুতি স্থির না হওয়া পর্যন্ত ফাংশনটি একটি নন-ব্লকিং উপায়ে বিরাম দেওয়া হয়। প্রতিশ্রুতি পূরণ হলে, আপনি মূল্য ফিরে পাবেন। প্রতিশ্রুতি প্রত্যাখ্যান করলে, প্রত্যাখ্যান করা মান নিক্ষেপ করা হয়।

ব্রাউজার সমর্থন

ব্রাউজার সমর্থন

  • ক্রোম: 55।
  • প্রান্ত: 15।
  • ফায়ারফক্স: 52।
  • সাফারি: 10.1।

উৎস

উদাহরণ: একটি আনয়ন লগিং

বলুন আপনি একটি URL আনতে চান এবং পাঠ্য হিসাবে প্রতিক্রিয়া লগ করুন৷ প্রতিশ্রুতি ব্যবহার করে এটি কীভাবে দেখায় তা এখানে:

function logFetch(url) {
  return fetch(url)
    .then((response) => response.text())
    .then((text) => {
      console.log(text);
    })
    .catch((err) => {
      console.error('fetch failed', err);
    });
}

এবং এখানে async ফাংশন ব্যবহার করে একই জিনিস:

async function logFetch(url) {
  try {
    const response = await fetch(url);
    console.log(await response.text());
  } catch (err) {
    console.log('fetch failed', err);
  }
}

এটি একই সংখ্যক লাইন, কিন্তু সমস্ত কলব্যাক চলে গেছে। এটি পড়া সহজ করে তোলে, বিশেষ করে যারা প্রতিশ্রুতির সাথে কম পরিচিত তাদের জন্য।

Async রিটার্ন মান

Async ফাংশন সর্বদা একটি প্রতিশ্রুতি প্রদান করে, আপনি await ব্যবহার করুন বা না করুন। যে প্রতিশ্রুতি async ফাংশন রিটার্ন যাই হোক না কেন সঙ্গে সমাধান, বা async ফাংশন নিক্ষেপ যাই হোক না কেন সঙ্গে প্রত্যাখ্যান. তাই এর সাথে:

// wait ms milliseconds
function wait(ms) {
  return new Promise((r) => setTimeout(r, ms));
}

async function hello() {
  await wait(500);
  return 'world';
}

hello() কল করা একটি প্রতিশ্রুতি প্রদান করে যা "world" এর সাথে পূরণ করে

async function foo() {
  await wait(500);
  throw Error('bar');
}

foo() কল করা একটি প্রতিশ্রুতি প্রদান করে যা Error('bar') দিয়ে প্রত্যাখ্যান করে

উদাহরণ: একটি প্রতিক্রিয়া স্ট্রিমিং

আরও জটিল উদাহরণে অ্যাসিঙ্ক ফাংশনের সুবিধা বৃদ্ধি পায়। বলুন আপনি অংশগুলি লগ আউট করার সময় একটি প্রতিক্রিয়া স্ট্রিম করতে চেয়েছিলেন এবং চূড়ান্ত আকারটি ফেরত দিতে চেয়েছিলেন।

এখানে এটি প্রতিশ্রুতি সহ:

function getResponseSize(url) {
  return fetch(url).then((response) => {
    const reader = response.body.getReader();
    let total = 0;

    return reader.read().then(function processResult(result) {
      if (result.done) return total;

      const value = result.value;
      total += value.length;
      console.log('Received chunk', value);

      return reader.read().then(processResult);
    });
  });
}

আমাকে দেখুন, জ্যাক "প্রতিশ্রুতির চালক" আর্কিবল্ড। দেখুন কিভাবে আমি একটি অ্যাসিঙ্ক্রোনাস লুপ সেট আপ করতে নিজের ভিতরে processResult() কল করছি? লেখাটি আমাকে খুব স্মার্ট মনে করেছে। কিন্তু বেশিরভাগ "স্মার্ট" কোডের মতো, এটি কী করছে তা বোঝার জন্য আপনাকে এটির দিকে তাকিয়ে থাকতে হবে, 90 এর দশকের সেই ম্যাজিক-আই ছবির মতো।

আসুন অ্যাসিঙ্ক ফাংশনগুলির সাথে আবার চেষ্টা করি:

async function getResponseSize(url) {
  const response = await fetch(url);
  const reader = response.body.getReader();
  let result = await reader.read();
  let total = 0;

  while (!result.done) {
    const value = result.value;
    total += value.length;
    console.log('Received chunk', value);
    // get the next result
    result = await reader.read();
  }

  return total;
}

সমস্ত "স্মার্ট" চলে গেছে। অ্যাসিঙ্ক্রোনাস লুপ যা আমাকে এত স্মাগ বোধ করেছে একটি বিশ্বস্ত, বিরক্তিকর, যখন-লুপ দিয়ে প্রতিস্থাপিত হয়েছে। অনেক ভালো। ভবিষ্যতে, আপনি অ্যাসিঙ্ক ইটারেটর পাবেন, যা while লুপকে একটি for-of লুপ দিয়ে প্রতিস্থাপন করবে, এটিকে আরও পরিষ্কার করে তুলবে।

অন্যান্য অ্যাসিঙ্ক ফাংশন সিনট্যাক্স

আমি আপনাকে ইতিমধ্যেই async function() {} দেখিয়েছি, কিন্তু async কীওয়ার্ড অন্য ফাংশন সিনট্যাক্সের সাথে ব্যবহার করা যেতে পারে:

তীর ফাংশন

// map some URLs to json-promises
const jsonPromises = urls.map(async (url) => {
  const response = await fetch(url);
  return response.json();
});

অবজেক্ট পদ্ধতি

const storage = {
  async getAvatar(name) {
    const cache = await caches.open('avatars');
    return cache.match(`/avatars/${name}.jpg`);
  }
};

storage.getAvatar('jaffathecake').then();

ক্লাস পদ্ধতি

class Storage {
  constructor() {
    this.cachePromise = caches.open('avatars');
  }

  async getAvatar(name) {
    const cache = await this.cachePromise;
    return cache.match(`/avatars/${name}.jpg`);
  }
}

const storage = new Storage();
storage.getAvatar('jaffathecake').then();

সাবধান! খুব ক্রমানুসারে যাওয়া এড়িয়ে চলুন

যদিও আপনি কোড লিখছেন যা সিঙ্ক্রোনাস দেখাচ্ছে, নিশ্চিত করুন যে আপনি সমান্তরালভাবে কাজ করার সুযোগটি মিস করবেন না।

async function series() {
  await wait(500); // Wait 500ms…
  await wait(500); // …then wait another 500ms.
  return 'done!';
}

উপরেরটি সম্পূর্ণ হতে 1000ms লাগে, যেখানে:

async function parallel() {
  const wait1 = wait(500); // Start a 500ms timer asynchronously…
  const wait2 = wait(500); // …meaning this timer happens in parallel.
  await Promise.all([wait1, wait2]); // Wait for both timers in parallel.
  return 'done!';
}

উপরেরটি সম্পূর্ণ হতে 500ms লাগে, কারণ উভয় অপেক্ষা একই সময়ে ঘটে। এর একটি বাস্তব উদাহরণ তাকান.

উদাহরণ: ক্রমানুসারে আউটপুট করা

বলুন আপনি একটি সিরিজ ইউআরএল আনতে চান এবং যত তাড়াতাড়ি সম্ভব সঠিক ক্রমে লগ করতে চান।

গভীর শ্বাস - প্রতিশ্রুতি সহ এটি দেখতে কেমন:

function markHandled(promise) {
  promise.catch(() => {});
  return promise;
}

function logInOrder(urls) {
  // fetch all the URLs
  const textPromises = urls.map((url) => {
    return markHandled(fetch(url).then((response) => response.text()));
  });

  // log them in order
  return textPromises.reduce((chain, textPromise) => {
    return chain.then(() => textPromise).then((text) => console.log(text));
  }, Promise.resolve());
}

হ্যাঁ, এটা ঠিক, আমি প্রতিশ্রুতি একটি ক্রম চেইন reduce ব্যবহার করছি. আমি খুব স্মার্ট . কিন্তু এটি একটি বিট তাই স্মার্ট কোডিং ছাড়া আপনি ভাল বন্ধ.

যাইহোক, উপরেরটিকে একটি অ্যাসিঙ্ক ফাংশনে রূপান্তর করার সময়, এটি খুব ক্রমানুসারে যেতে লোভনীয়:

প্রস্তাবিত নয় - খুব ক্রমিক
async function logInOrder(urls) {
  for (const url of urls) {
    const response = await fetch(url);
    console.log(await response.text());
  }
}
দেখতে অনেক সুন্দর, কিন্তু আমার প্রথম আনা সম্পূর্ণরূপে পড়া না হওয়া পর্যন্ত আমার দ্বিতীয় আনা শুরু হয় না, এবং তাই। এটি প্রতিশ্রুতির উদাহরণের তুলনায় অনেক ধীর যা সমান্তরালভাবে ফেচগুলি সম্পাদন করে৷ সৌভাগ্যক্রমে একটি আদর্শ মধ্যম স্থল আছে.
প্রস্তাবিত - সুন্দর এবং সমান্তরাল
function markHandled(...promises) {
  Promise.allSettled(promises);
}

async function logInOrder(urls) {
  // fetch all the URLs in parallel
  const textPromises = urls.map(async (url) => {
    const response = await fetch(url);
    return response.text();
  });

  markHandled(...textPromises);

  // log them in sequence
  for (const textPromise of textPromises) {
    console.log(await textPromise);
  }
}
এই উদাহরণে, ইউআরএলগুলি আনা হয় এবং সমান্তরালভাবে পড়া হয়, কিন্তু "স্মার্ট" reduce বিটটি একটি স্ট্যান্ডার্ড, বিরক্তিকর, পড়ার জন্য লুপ দিয়ে প্রতিস্থাপিত হয়।

ব্রাউজার সমর্থন সমাধান: জেনারেটর

আপনি যদি জেনারেটর সমর্থন করে এমন ব্রাউজারগুলিকে লক্ষ্য করে থাকেন (যা প্রতিটি প্রধান ব্রাউজারের সর্বশেষ সংস্করণ অন্তর্ভুক্ত করে) আপনি পলিফিল অ্যাসিঙ্ক ফাংশনগুলি সাজাতে পারেন৷

Babel আপনার জন্য এটি করবে, এখানে Babel REPL এর মাধ্যমে একটি উদাহরণ রয়েছে৷

আমি ট্রান্সপিলিং পদ্ধতির পরামর্শ দিচ্ছি, কারণ আপনার টার্গেট ব্রাউজারগুলি অ্যাসিঙ্ক ফাংশন সমর্থন করলে আপনি এটি বন্ধ করতে পারেন, কিন্তু আপনি যদি সত্যিই একটি ট্রান্সপিলার ব্যবহার করতে না চান তবে আপনি ব্যাবেলের পলিফিল নিতে পারেন এবং এটি নিজে ব্যবহার করতে পারেন। এর পরিবর্তে:

async function slowEcho(val) {
  await wait(1000);
  return val;
}

…আপনি পলিফিল অন্তর্ভুক্ত করবেন এবং লিখবেন:

const slowEcho = createAsyncFunction(function* (val) {
  yield wait(1000);
  return val;
});

মনে রাখবেন যে আপনাকে createAsyncFunction করতে একটি জেনারেটর ( function* ) পাস করতে হবে এবং await পরিবর্তে yield ব্যবহার করতে হবে। তা ছাড়া এটি একই কাজ করে।

ওয়ার্কঅ্যারাউন্ড: পুনর্জন্মকারী

আপনি যদি পুরানো ব্রাউজারগুলিকে টার্গেট করেন, তবে ব্যাবেল জেনারেটরগুলিকে ট্রান্সপিল করতে পারে, যা আপনাকে IE8 পর্যন্ত অ্যাসিঙ্ক ফাংশনগুলি ব্যবহার করার অনুমতি দেয়। এটি করার জন্য আপনার প্রয়োজন Babel এর es2017 প্রিসেট এবং es2015 প্রিসেট

আউটপুট ততটা সুন্দর নয় , তাই কোড-ব্লোটের জন্য সতর্ক থাকুন।

সব জিনিস অ্যাসিঙ্ক!

সমস্ত ব্রাউজার জুড়ে async ফাংশন ল্যান্ড করার পরে, প্রতিটি প্রতিশ্রুতি-রিটার্নিং ফাংশনে সেগুলি ব্যবহার করুন! তারা শুধুমাত্র আপনার কোড পরিপাটি করে না, কিন্তু এটি নিশ্চিত করে যে ফাংশন সর্বদা একটি প্রতিশ্রুতি প্রদান করবে।

আমি 2014 সালে অ্যাসিঙ্ক ফাংশন সম্পর্কে সত্যিই উত্তেজিত হয়েছিলাম, এবং ব্রাউজারে বাস্তবে তাদের ল্যান্ড করতে দেখে খুব ভালো লাগছে। উফ!