كيف يمكن إرجاع response من نداء غير متزامن في JavaScript؟

كيف يمكن إرجاع response من نداء غير متزامن في JavaScript؟

في JavaScript، تعد العمليات غير المتزامنة جزءًا أساسيًا من التعامل مع المهام التي تستغرق وقتًا في التنفيذ، مثل طلب بيانات من خادم خارجي أو التعامل مع الملفات أو مؤقتات الانتظار. العمليات غير المتزامنة تختلف عن العمليات العادية المتتابعة في أن المتصفح لا يتوقف في انتظار نتيجتها، بل يستمر في تنفيذ باقي التعليمات، وعند انتهاء العملية يتم تنفيذ كود معين لاستلام النتيجة. وهنا يظهر السؤال: كيف يمكن إعادة النتيجة من نداء غير متزامن بطريقة صحيحة؟

لتحقيق ذلك، وفّرت JavaScript طريقتين أساسيتين: إما باستخدام Promise مع then أو من خلال async/await. وفيما يلي توضيح عملي لطريقتي التنفيذ.

أولًا: ما هو النداء غير المتزامن؟

هو ببساطة عملية تنفّذ في الخلفية ولا ينتظرها البرنامج حتى تنتهي قبل أن يُكمل تنفيذ الأوامر التالية. مثال ذلك: عند إرسال طلب بيانات باستخدام fetch، لن ينتظر المتصفح وصول الرد ليكمل باقي الكود، بل سيواصل التنفيذ ويُكمل الطلب في الخلفية.

الطريقة الأولى: التعامل باستخدام Promise مع then

عند استدعاء عملية غير متزامنة مثل fetch، فإنها تُعيد كائن من نوع Promise. هذا الكائن يحتوي على نتيجة العملية بمجرد اكتمالها. ويمكن استخدام then لاستلام هذه النتيجة.

مثال:

function getData() {
  return fetch('https://api.example.com/data')
    .then(response => response.json())
    .then(data => {
      console.log(data);
      return data;
    });
}

getData().then(result => {
  console.log('Received data:', result);
});

في هذا المثال:

  • تقوم دالة getData بطلب بيانات من عنوان معين.

  • تستخدم then لمعالجة الاستجابة وتحويلها إلى JSON.

  • يتم إرجاع البيانات.

  • عند استدعاء getData() يتم التعامل مع النتيجة النهائية باستخدام then.

ملاحظة:

لا يمكن إعادة النتيجة مباشرة من داخل then إلى خارج الدالة المتزامنة، لذا يجب إرجاع Promise نفسه والتعامل مع نتيجته عند استدعاء الدالة.

الطريقة الثانية: استخدام async/await

تعتبر طريقة async/await أكثر وضوحًا وحداثة في كتابة الكود. إذ تتيح انتظار نتيجة العملية غير المتزامنة بطريقة تشبه الكود المتزامن العادي، مما يجعل القراءة والفهم أسهل.

مثال:

async function getData() {
  const response = await fetch('https://api.example.com/data');
  const data = await response.json();
  return data;
}

getData().then(result => {
  console.log('Received data:', result);
});

في هذا المثال:

  • تم تحويل دالة getData إلى دالة غير متزامنة باستخدام async.

  • باستخدام await تم الانتظار لحين انتهاء تنفيذ fetch.

  • بعد الحصول على الاستجابة تم تحويلها إلى JSON.

  • تم إرجاع البيانات.

  • عند استدعاء الدالة، يتم التعامل مع Promise الناتجة عن الدالة باستخدام then.

فوائد استخدام async/await:

  • كود أكثر وضوحًا وسهولة في القراءة.

  • تسلسل منطقي يشبه الكود المتزامن.

  • إمكانية التعامل مع الأخطاء بسهولة من خلال try-catch.

مثال مع try-catch:

async function getData() {
  try {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    return data;
  } catch (error) {
    console.error('Error fetching data:', error);
  }
}

خطأ شائع عند التعامل مع العمليات غير المتزامنة

كثير من المبتدئين يقعون في خطأ محاولة استرجاع القيمة من Promise بشكل مباشر داخل دالة متزامنة، معتقدين أن القيمة ستكون متوفرة لحظة إرجاعها.

مثال خاطئ:

function getData() {
  let result;
  fetch('https://api.example.com/data')
    .then(response => response.json())
    .then(data => result = data);
  return result; // ستعود undefined
}

console.log(getData());

في هذا المثال، عند تنفيذ getData() سيتم تنفيذ fetch في الخلفية، بينما الدالة سترجع undefined مباشرة قبل اكتمال عملية جلب البيانات.

الحل الصحيح:

يجب إرجاع Promise نفسه:

function getData() {
  return fetch('https://api.example.com/data')
    .then(response => response.json());
}

getData().then(data => console.log(data));

أو باستخدام async/await:

async function getData() {
  const response = await fetch('https://api.example.com/data');
  const data = await response.json();
  return data;
}

getData().then(data => console.log(data));

متى تستخدم then ومتى تستخدم async/await؟

  • إذا كان الكود بسيطًا ولا يتضمن العديد من العمليات المتداخلة، يمكن استخدام then.

  • إذا كان الكود يحتوي على عدة عمليات غير متزامنة متتابعة، من الأفضل استخدام async/await لأنه أكثر وضوحًا وأسهل في القراءة.

ملاحظات مهمة:

  • الدوال التي تحتوي على await يجب أن يُصرح بها باستخدام async.

  • دوال async دائمًا تعيد Promise.

  • يمكن استخدام await فقط داخل دوال async.

بهذا الشكل يصبح التعامل مع العمليات غير المتزامنة في JavaScript واضحًا وسهل التطبيق باستخدام طريقتين أساسيتين، مع ضرورة تجنب الخطأ الشائع المتعلق بمحاولة استرجاع قيمة من Promise داخل دالة متزامنة.

حول المحتوى:

في هذا المقال، سنتعرّف على كيفية إعادة الاستجابة من نداء غير متزامن في JavaScript، مع شرح مبسّط للأدوات الأساسية مثل Promises، و async/await، وبعض الأخطاء الشائعة التي يقع فيها المبرمجون أثناء التعامل مع هذا النمط.