كيفية إرجاع رد من استدعاء غير متزامن: دليل كامل لـ JS

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

«أقوم بتشغيل دالة، لكنها ترجع undefined!» — ألم مألوف؟

\n

تخيل: أنت تكتب كوداً بلغة JavaScript، تقوم بطلب إلى API، تنتظر البيانات… وتحصل في المقابل على undefined. أو الأسوأ — Promise {<pending>}. تستسلم، وتريد إغلاق المتصفح والذهاب للاعتكاف.

\n

لا تتعجل. هذه المشكلة تؤرق حتى المطورين ذوي الخبرة. ليس من قبيل الصدفة أن سؤال «How do I return the response from an asynchronous call?» على Stack Overflow حصد آلاف المشاهدات ومئات الإجابات. اليوم سنحل هذه المشكلة نهائياً.

\n

في هذه المقالة ستتعلم:

\n
    \n
  • لماذا لا يعمل return بالطريقة التي اعتدت عليها
  • \n
  • كيف تروض عدم التزامن باستخدام دوال الاسترجاع، الوعود، و async/await
  • \n
  • أي نهج تختار لمشروعك
  • \n
\n

هيا بنا!

\n\n

لماذا لا يعيد return البيانات؟

\n

دعنا نذهب مباشرة إلى صلب الموضوع. إليك مثالاً كلاسيكياً:

\n\n
function getUserData(userId) {\n  fetch(`https://api.example.com/users/${userId}`)\n    .then(response => response.json())\n    .then(data => {\n      return data; // يبدو أن كل شيء على ما يرام؟\n    });\n}\n\nconst user = getUserData(42);\nconsole.log(user); // undefined
\n\n

مألوف؟ لقد كتبت return data داخل .then()، لكنك حصلت على undefined في الخارج. لماذا؟

\n

JavaScript — أحادي الخيط وغير مانع. عندما تستدعي fetch، فإنه لا ينتظر رد الخادم. بدلاً من ذلك، «يؤجل» تنفيذ دالة الاسترجاع جانباً وينتقل فوراً إلى السطر التالي. والسطر التالي هو console.log(user)، حيث user لم يُحدد بعد.

\n

دالتك getUserData تنتهي قبل أن يصل رد الخادم. إنها لا تُرجع شيئاً (تُرجع undefined)، والبيانات «معلقة» في الهواء.

\n

كيف نصلح هذا؟ هناك ثلاث طرق رئيسية.

\n\n

الطريقة 1: دوال الاسترجاع (Callbacks) — جد عدم التزامن

\n

سابقاً، قبل ظهور الوعود، كانت جميع العمليات غير المتزامنة تتم عبر دوال الاسترجاع. تقوم بتمرير دالة سيتم استدعاؤها عند وصول البيانات.

\n\n
function getUserData(userId, callback) {\n  fetch(`https://api.example.com/users/${userId}`)\n    .then(response => response.json())\n    .then(data => {\n      callback(data); // نستدعي دالة الاسترجاع مع البيانات\n    })\n    .catch(error => {\n      callback(null, error); // أو نمرر الخطأ\n    });\n}\n\n// الاستخدام:\ngetUserData(42, (data, error) => {\n  if (error) {\n    console.error('خطأ:', error);\n    return;\n  }\n  console.log('بيانات المستخدم:', data);\n});
\n\n

الإيجابيات: البساطة، تعمل في كل مكان.

\n

السلبيات: إذا قمت بتداخل عدة دوال استرجاع داخل بعضها، ستحصل على «جحيم دوال الاسترجاع» (callback hell). يصبح الكود غير قابل للقراءة.

\n\n
getUserData(42, (data1) => {\n  getPosts(data1.id, (data2) => {\n    getComments(data2[0].id, (data3) => {\n      // ... وهكذا دواليك\n    });\n  });\n});
\n\n

JavaScript الحديثة تخلت عن هذا. لكن فهم دوال الاسترجاع ضروري — فهي أساس جميع النهج الأخرى.

\n\n

الطريقة 2: الوعود (Promises) — منقذو قابلية القراءة

\n

الوعد هو كائن يمثل «تعهداً» بإرجاع قيمة في المستقبل. له ثلاث حالات: pending (انتظار)، fulfilled (تم التنفيذ)، و rejected (مرفوض).

\n

لنعد إلى مثالنا. بدلاً من دالة الاسترجاع، نعيد الوعد نفسه:

\n\n
function getUserData(userId) {\n  return fetch(`https://api.example.com/users/${userId}`)\n    .then(response => {\n      if (!response.ok) {\n        throw new Error('الشبكة ليست في حالة جيدة');\n      }\n      return response.json();\n    });\n}\n\n// الاستخدام:\nconst userPromise = getUserData(42);\n\nuserPromise\n  .then(data => {\n    console.log('بيانات المستخدم:', data);\n  })\n                

المدونات

توصيات الكتب