---
title: 'إتقان JavaScript أنماط غير متزامنة: من مكالمات العودة إلى التزامن'
description: استكشف كيف JavaScript تطور من callbacks إلى التزامن المنظم. تعلم async/await، والوعود، وأنماط تزامن متقدمة لتطبيقات قابلة للتوسع وجاهزة للإنتاج.
url: https://nerdleveltech.com/ar/mastering-javascript-async-patterns-from-callbacks-to-concurrency
language: ar
date: '2025-11-11'
category: backend
tags:
  - JavaScript
  - Async
  - Concurrency
  - Node.js
  - Promises
  - Async/Await
  - Web Development
markdown_url: https://nerdleveltech.com/ar/mastering-javascript-async-patterns-from-callbacks-to-concurrency.md
attribution: 'Source: NerdLevelTech — https://nerdleveltech.com/ar/mastering-javascript-async-patterns-from-callbacks-to-concurrency'
terms: https://nerdleveltech.com/llms.txt
---

# إتقان JavaScript أنماط غير متزامنة: من مكالمات العودة إلى التزامن

## TL;DR

- JavaScript’s التطور غير المتزامن: **كولباك → وعود → async/await → التزامن المنظم**.
- **حلقة الأحداث** و**طابور المهام الصغيرة** تُعزز الأداء غير المعيق.
- إتقان البرمجة غير المتزامنة = التحكم في الإلغاء، التنسيق، والصحة.
- أنماط البرمجة غير المتزامنة الحديثة تُحسّن القابلية للتوسع والموثوقية لكنها تتطلب معالجة أخطاء منهجية.
- اختيار الأداة المناسبة للبرمجة غير المتزامنة هو المفتاح لكتابة كود إنتاجي وقابل للصيانة.

---

## ما ستتعلمه

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

---

## المتطلبات الأساسية

قبل الغوص في الموضوع، تأكد من أنك مرتاح مع:

- مفاهيم JavaScript الأساسية (الدوال، الإغلاق، الكائنات)
- بنية ES6+ (دوال السهم، التفكيك)
- واجهات برمجة التطبيقات الأساسية لـ Node.js أو المتصفح

إذا كنت قد كتبت من قبل `async function` أو استخدمت `fetch()`, فأنت مستعد.

---

## مقدمة: لماذا البرمجة غير المتزامنة مهمة

JavaScript يعمل على **خيط واحد**—مكدس استدعاء واحد، حلقة أحداث واحدة. ومع ذلك، تتعامل التطبيقات الحديثة مع مئات العمليات المتزامنة: طلبات الشبكة، قراءة الملفات، تفاعلات المستخدم، والمهام الخلفية. بدون البرمجة غير المتزامنة، ستوقف كل عملية إدخال/إخراج الخيط، مما يؤدي إلى تجمد الواجهة أو إيقاف طلبات الخلفية.

البرمجة غير المتزامنة هي ما تجعل JavaScript **متجاوبة**. هكذا تستطيع تطبيقات الويب جلب البيانات أثناء تحديث DOM، أو كيف يمكن لـ Node.js API خدمة آلاف الطلبات المتزامنة دون إنشاء خيوط جديدة[^1].

لكن البرمجة غير المتزامنة ليست مجرد بناء جملي—بل هي عن **التحكم**: معرفة متى تبدأ المهام، تنتهي، وتفشل. لإتقان JavaScript غير المتزامن، نحتاج إلى فهم كيفية تطور أنماطه.

---

## تطور البرمجة غير المتزامنة في JavaScript

| النمط                         | العصر   | أسلوب الصياغة    | معالجة الأخطاء | الوضوح | الاستخدام النموذجي                               |
| ----------------------------- | ------- | ---------------- | -------------- | ------ | ------------------------------------------------ |
| كولباك                        | ES3     | دوال متداخلة     | يدوي           | ضعيف   | واجهات Node.js القديمة                           |
| الوعود                        | ES6     | متسلسل `.then()` | `.catch()`     | متوسط  | طلبات HTTP، واجهات برمجة التطبيقات غير المتزامنة |
| async/await                   | ES2017  | `async`/`await`  | `try/catch`    | ممتاز  | تدفق التحكم غير المتزامن الحديث                  |
| التدفقات، العاملين، المراقبين | ES2020+ | موجه بالأحداث    | سياقي          | متغير  | أنظمة عالية الإنتاجية أو تفاعلية                 |

كل خطوة حسّنت القابلية للتركيب وتجربة المطور مع إدخال تنازلات جديدة في الأداء ومعالجة الأخطاء.

---

## فهم حلقة الأحداث

قبل الغوص في الأنماط، دعونا نصور **حلقة الأحداث**، القلب النابض لنموذج البرمجة غير المتزامن في JavaScript.

```mermaid
flowchart TD
  A[Call Stack] -->|Empty| B[Event Loop]
  B --> C[Callback Queue]
  B --> D[Microtask Queue]
  D -->|Higher Priority| A
  C -->|Executed Next| A
```

- **مكدس الاستدعاءات**: ينفذ الكود المتزامن.
- **طابور المهام الصغيرة**: يحتوي على كولباك الوعود واستمراريات async/await.
- **طابور الكولباك (طابور المهام الكبيرة)**: يحتوي على إدخال/إخراج، مؤقتات، وأحداث DOM.

تتحقق حلقة الأحداث باستمرار مما إذا كان مكدس الاستدعاءات فارغًا. عندما يكون كذلك، تعالج أولاً **جميع المهام الصغيرة** قبل الانتقال إلى المهام الكبيرة[^2]. هذا يفسر لماذا يعمل وعده مُحَلَّل قبل كولباك `setTimeout()` في نفس التكتكة.

---

## النمط 1: كولباك

### كيف يعمل

كانت الكولباك أول آلية غير متزامنة في JavaScript: تمرير دالة لتنفيذها عند اكتمال العملية.

```JavaScript
import fs from 'fs';

fs.readFile('data.txt', 'utf8', (err, data) => {
  if (err) return console.error('Error:', err);
  console.log('File contents:', data);
});
```

### المشكلة: جحيم الكولباك

عندما تعتمد المهام على بعضها، تصبح الكولباك متداخلة بعمق:

```JavaScript
getUser(id, (err, user) => {
  if (err) return handleError(err);
  getPosts(user.id, (err, posts) => {
    if (err) return handleError(err);
    getComments(posts[0].id, (err, comments) => {
      console.log(comments);
    });
  });
});
```

هذا «هرم الشر» يجعل تصحيح الأخطاء واختبار النظام ومعالجة الأخطاء صعبًا.

### متى تستخدم

- مهام غير متزامنة بسيطة وفريدة.
- واجهات برمجة Node.js القديمة التي لم يتم تحويلها إلى وعود.

### متى لا تستخدم

- التدفقات المعقدة أو السلاسل المتزامنة المعتمدة.
- قواعد الكود التي تتطلب قابلية صيانة عالية.

---

## النمط الثاني: الوعود

الوعود، التي تم تقديمها في ES6، حلّت جحيم الكولباك. الوعد يمثل قيمة قد تكون متاحة الآن، لاحقًا، أو أبدًا.

```JavaScript
fetch('https://API.example.com/data')
  .then(res => res.json())
  .then(data => console.log('Data:', data))
  .catch(err => console.error('Error:', err));
```

### الميزات الرئيسية

- قابلة للربط والتركيب.
- تدفقات غير متزامنة مسطحة.
- نشر الأخطاء المدمج.

### الخطأ الشائع: الرفضات غير المعالجة

إذا نسيت `.catch()`، يمكن أن تؤدي الرفضات غير المعالجة إلى إيقاف عمليات Node.js في الإصدارات الحديثة[^3].

**الحل:** قم دائمًا بإرفاق `.catch()` أو استخدام معالج عالمي:

```JavaScript
process.on('unhandledRejection', err => {
  console.error('Unhandled rejection:', err);
});
```

### ملاحظة الأداء

الوعود تُدخل تحميلًا من المهام الدقيقة. بالنسبة لأحمال العمل المحدودة بـI/O، فهو ضئيل؛而对于 مهام المعالجة الثقيلة، يمكن أن يضيف تأخيرًا طفيفًا.

---

## النمط الثالث: Async/Await

تم تقديمها في ES2017، جعل async/await الكود غير المتزامن يبدو متزامنًا—دون حظر.

```JavaScript
async function fetchUserData(id) {
  try {
    const res = await fetch(`/API/users/${id}`);
    return await res.json();
  } catch (err) {
    console.error('Failed to fetch user:', err);
  }
}
```

### لماذا هو أفضل

- تدفق تحكم خطي وسهل القراءة.
- معالجة الأخطاء المدمجة مع `try/catch`.
- مثالي للمنطق غير المتزامن المتسلسل.

### متى تستخدم

- عندما تعتمد خطوات غير متزامنة على النتائج السابقة.
- لقواعد كود واضحة وقابلة للصيانة.

### متى لا تستخدم

- لمهام مستقلة يمكن تشغيلها بالتوازي—استخدم `Promise.all()` بدلاً من ذلك.

### مثال: موازاة المهام غير المتزامنة

**قبل (متسلسل):**

```JavaScript
async function loadData() {
  const user = await fetchUser();
  const posts = await fetchPosts(user.id);
  const comments = await fetchComments(posts[0].id);
  return { user, posts, comments };
}
```

**بعد (متوازي):**

```JavaScript
async function loadData() {
  const [user, posts] = await Promise.all([fetchUser(), fetchPosts()]);
  const comments = await fetchComments(posts[0].id);
  return { user, posts, comments };
}
```

موازاة المهام المستقلة تقلل عادةً التأخير في السيناريوهات المحدودة بالإدخال/الإخراج[^5].

---

## أنماط متقدمة: التدفقات، العمال، والمراقبات

### التدفقات

تتعامل تدفقات Node.js مع البيانات الكبيرة بكفاءة دون تحميلها كلها في الذاكرة.

```JavaScript
import fs from 'fs';

const readStream = fs.createReadStream('bigfile.log');
readStream.on('data', chunk => console.log('Received', chunk.length, 'bytes'));
readStream.on('end', () => console.log('Done'));
```

التدفقات تُصدر أحداثًا بشكل غير متزامن أثناء تدفق البيانات[^6].

### Web Workers

في المتصفحات، **Web Workers** تشغل النصوص البرمجية في خيوط خلفية وتتواصل بشكل غير متزامن.

```JavaScript
// main.js
const worker = new Worker('worker.js');
worker.postMessage({ task: 'compute' });
worker.onmessage = e => console.log('Result:', e.data);
```

### Observables

انتشرت بواسطة RxJS، المراقبات توفر تدفقات بيانات غير متزامنة على مر الزمن—مثالية للتطبيقات في الوقت الحقيقي.

```JavaScript
import { fromEvent } from 'rxjs';

fromEvent(document, 'click').subscribe(event => console.log('Clicked!', event));
```

---

## متى تستخدم مقابل متى لا تستخدم

| النمط            | عند استخدامه                            | عند تجنبه          |
| ---------------- | --------------------------------------- | ------------------ |
| الدوال العودية   | واجهات قديمة، تفاعلات غير متزامنة بسيطة | تدفقات معقدة       |
| الوعود           | منطق غير متزامن متسلسل                  | تزامن ثقيل         |
| غير متزامن/انتظر | منطق غير متزامن متسلسل                  | حلقات تزامن عالية  |
| التدفقات         | بيانات كبيرة أو مستمرة                  | قراءات صغيرة فردية |
| العمال           | مهام مقيدة بالمعالج                     | تحديثات DOM بسيطة  |
| المراقبات        | في الوقت الحقيقي، قائمة على الأحداث     | جلب بيانات ثابتة   |

---

## مثال عملي: التزامن غير المتزامن في الإنتاج

تستخدم خدمات Node.js الكبيرة غالبًا غير متزامن/انتظر مع أدوات التحكم في التزامن مثل `Promise.allSettled()` ومجموعات مخصصة للحفاظ على الإنتاجية دون حظر دورة الأحداث[^7].

### مثال على التزامن المُحكم

```JavaScript
async function fetchInBatches(urls, limit = 5) {
  const results = [];
  while (urls.length) {
    const batch = urls.splice(0, limit);
    const responses = await Promise.allSettled(batch.map(url => fetch(url)));
    results.push(...responses);
  }
  return results;
}
```

هذه الطريقة توازن التزامن لتجنب إرهاق واجهات برمجة التطبيقات أو استنفاد الذاكرة.

---

## المزالق الشائعة والحلول

| المزالق                    | الوصف                  | الحل                              |
| -------------------------- | ---------------------- | --------------------------------- |
| رفض غير معالج              | مفقود `.catch()`       | يجب دائمًا ربط معالجات الأخطاء    |
| حظر حلقة الأحداث           | مهام ثقيلة على المعالج | استخدم Workers أو عمليات فرعية    |
| استدعاءات متتالية أسينكرون | `await` داخل الحلقات   | استخدم `Promise.all()` أو التجميع |
| تسريب الذاكرة              | وعود غير محلولة        | استخدم فترات انتظار أو رموز إلغاء |

### مثال: تجنب استخدام Await في الحلقات

**سيء:**

```JavaScript
for (const id of ids) {
  await fetchUser(id); // sequential
}
```

**جيد:**

```JavaScript
await Promise.all(ids.map(id => fetchUser(id))); // parallel
```

---

## أنماط معالجة الأخطاء

### 1. محاولة/التقاط مع الدوال غير المتزامنة

```JavaScript
try {
  const data = await fetchData();
} catch (err) {
  console.error('Error:', err);
}
```

### 2. معالجة الرفض العالمي

```JavaScript
process.on('unhandledRejection', err => {
  console.error('Unhandled rejection:', err);
});
```

### 3. التدهور اللطيف

```JavaScript
const results = await Promise.allSettled(tasks);
results.forEach(r => {
  if (r.status === 'fulfilled') console.log(r.value);
  else console.warn('Failed:', r.reason);
});
```

---

## اختبار الكود غير المتزامن

### مثال باستخدام Jest

```JavaScript
test('fetches data correctly', async () => {
  const data = await fetchData();
  expect(data).toHaveProperty('id');
});
```

**نصيحة احترافية:** دائماً `return` أو `await` للوعود في الاختبارات — وإلا قد ينتهي الاختبار قبل أن يُحل الكود غير المتزامن.

---

## المراقبة والقابلية للملاحظة

يمكن لأخطاء غير المتزامنة أن تختفي بصمت. أضف تسجيلًا منظمًا وتتبعًا.

```JavaScript
import pino from 'pino';
const logger = pino();

async function processOrder(orderId) {
  logger.info({ orderId }, 'Processing order');
  try {
    const result = await chargeCustomer(orderId);
    logger.info({ orderId, result }, 'Order processed');
  } catch (err) {
    logger.error({ orderId, err }, 'Order failed');
  }
}
```

استخدم أدوات مثل **OpenTelemetry** أو **Datadog APM** لتتبع الفترات غير المتزامنة عبر الأنظمة الموزعة[^8].

---

## اعتبارات الأمان

- **تجنب حالات السباق:** استخدم قفلًا أو طوابير عندما تقوم العمليات غير المتزامنة بتعديل الحالة المشتركة.
- **تحقق من المدخلات غير المتزامنة:** لا تثق أبدًا بالبيانات من المصادر غير المتزامنة (مثل واجهات برمجة التطبيقات أو البث).
- **منع هجمات DoS:** قم بتحديد التزامن وضبط فترات الانتظار لتجنب استنفاد الموارد.

---

## رؤى حول القابلية للتوسع

تتسع أنماط غير المتزامنة أفقيًا — تتيح مزيدًا من العمليات المتزامنة دون إضافة خيوط. ومع ذلك:

- `Promise.all()` يمكن أن تُثقل واجهات برمجة التطبيقات إذا استُخدمت بدون حدود.
- التدفقات تتوسع جيدًا لأنابيب البيانات الثقيلة.
- العمال يوسعون المهام المرتبطة بالمعالج عبر النوى.

تُستخدم هذه الأنماط على نطاق واسع في خدمات Node.js واسعة النطاق لتحقيق تزامن عالٍ وإنتاجية عالية[^7].

---

## دليل استكشاف الأخطاء وإصلاحها

| الأعراض             | السبب المحتمل            | الحل                        |
| ------------------- | ------------------------ | --------------------------- |
| تجمد التطبيق        | كود متزامن يعيق          | نقل إلى Worker أو async API |
| تسرب الذاكرة        | وعود غير محلولة          | استخدم مغلفات تايم آوت      |
| API أخطاء حد السرعة | طلبات متزامنة كثيرة جدًا | أضف محدد التزامن            |
| سجلات مفقودة        | أخطاء async مبتلعة       | أضف معالجات رفض عالمية      |

---

## الأخطاء الشائعة التي يرتكبها الجميع

1. استخدام `await` داخل الحلقات بشكل غير ضروري.
2. نسيان التعامل مع الوعود المرفوضة.
3. خلط الدعوات العودية والوعود في نفس الدالة.
4. الافتراض أن async/await يجعل الكود متوازيًا (لا يفعل ذلك افتراضيًا).
5. تجاهل إلغاء العملية ومعالجة التايم آوت.

---

## تحدي جربه بنفسك

اكتب دالة تقوم بما يلي:

1. يجلب عدة واجهات برمجة تطبيقات بشكل متزامن.
2. يعيد محاولة الطلبات الفاشلة حتى 3 مرات.
3. يعيد جميع الاستجابات الناجحة.

**تلميح:** دمج `Promise.allSettled()` مع مغلف إعادة المحاولة.

---

## النقاط الرئيسية

> **إتقان async = التحكم، الوضوح، والتزامن.**
>
> - استخدم async/await للقراءة، والوعود للتركيب.
> - تعامل دائمًا مع الرفض والتوقيت.
> - راقب تدفقات async — الأخطاء الصامتة يمكن أن تُضعف الأداء.
> - قم بالتوسع بأمان مع تحكم في التزامن.

---

## أسئلة شائعة

**س1. هل يجعل async/await JavaScript متعدد الخيوط؟**\
&#x20;لا. يجعل I/O غير معيق أسهل في التعبير. JavaScript لا يزال يعمل على خيط واحد لكل حلقة حدث[^2].

**س2. ما الفرق بين المهام الصغرى والمهام الكبرى؟**\
&#x20;المهام الصغرى (الوعود) تُنفَّذ قبل المهام الكبرى (الموقِّتات، I/O). هذا يؤثر على ترتيب التنفيذ.

**س3. هل الدوال async أسرع من الوعود؟**\
&#x20;هي سكر بنائي فوق الوعود. الأداء مشابه تقريبًا.

**س4. كيف ألغي عملية async؟**\
&#x20;استخدم `AbortController` (لـ `fetch`) أو منطق إلغاء مخصص.

**س5. هل يمكن خلط الدعوات العودية مع الوعود؟**\
&#x20;يمكنك ذلك، لكن من الأفضل تحويل الدعوات العودية إلى وعود باستخدام `util.promisify()`.

---

## الخطوات التالية

- استكشف مكتبات التحكم في التزامن مثل `p-limit` أو `Bottleneck`.
- تعلم عن Observable (RxJS) للبرمجة التفاعلية async.
- أضف تتبع OpenTelemetry إلى خدمات async الخاصة بك.

إذا أعجبك هذا الاستعراض المعمق، اشترك للبقاء على اطلاع على ممارسات هندسة JavaScript الحديثة.

---

[^1]: وثائق Node.js – عن حلقة الحدث <https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/>

[^2]: وثائق MDN Web – حلقة الحدث <https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop>

[^3]: وثائق Node.js – رفض الوعود غير المعالجة <https://nodejs.org/API/process.html#event-unhandledrejection>

[^4]: مدونة V8 – JavaScript الوعود والمهام الصغرى <https://v8.dev/blog/fast-async>

[^5]: Web.dev – تحسين Async JavaScript <https://web.dev/promises/>

[^6]: وثائق تدفقات Node.js API <https://nodejs.org/API/stream.html>

[^7]: مدونة Netflix Tech – توسيع نطاق ميكروسيرفس Node.js <https://netflixtechblog.com/scaling-node-js-microservices-at-netflix-7a5e1d5b7e5e>

[^8]: OpenTelemetry JavaScript الوثائق <https://opentelemetry.io/docs/instrumentation/js/>
