Frontend Development

تسلط بر الگوهای ناهمگام (Async) در جاوااسکریپت

برنامه‌نویسی ناهمگام ستون فقرات توسعه وب مدرن است. اگرچه وعده‌ها (Promises) و سینتکس async/await استاندارد شده‌اند، اما برنامه‌های واقعی اغلب نیاز به کنترل پیشرفته‌تر بر جریان اجرا، مقاومت در برابر خطاها و مدیریت منابع دارند. در این راهنما، الگوهای پیشرفته ناهمگامی را بررسی می‌کنیم که شکاف بین عملکرد پایه و استحکام در سطح تولید را پر می‌کنند.

1. کنترل همزمانی با دسته‌بندی

یکی از رایج‌ترین دام‌ها در توسعه فرانت‌اند، ارسال همزمان صدها درخواست API است. این کار می‌تواند سرور را تحت فشار قرار دهد، پهنای باند کاربر را مستهلک کند یا خطاهای محدودیت نرخ (Rate-limiting) را فعال کند. برای کاهش این مشکل، یک محدودکننده همزمانی پیاده‌سازی می‌کنیم. این ابزار تضمین می‌کند که تنها تعداد مشخصی از وعده‌ها (Promises) در هر لحظه فعال باشند.

در اینجا یک پیاده‌سازی عملی از یک اجراکننده با کنترل همزمانی آورده شده است:

function runConcurrently(tasks, limit) {
  const results = [];
  let index = 0;

  function next() {
    if (index >= tasks.length) return Promise.resolve();
    
    const currentIndex = index;
    index++;
    
    return tasks[currentIndex]().then((result) => {
      results[currentIndex] = result;
      return next();
    });
  }

  // ایجاد یک گروه از 'limit' کارگر
  const workers = Array.from({ length: limit }, () => next());
  
  return Promise.all(workers).then(() => results);
}

// مثال استفاده
const tasks = [
  () => fetch('/api/data-1').then(r => r.json()),
  () => fetch('/api/data-2').then(r => r.json()),
  () => fetch('/api/data-3').then(r => r.json()),
];

runConcurrently(tasks, 2) // فقط ۲ درخواست همزمان
  .then(console.log);

این الگو زمانی که با مجموعه‌های داده بزرگ سروکار دارید که باید به صورت دسته‌ای دریافت شوند، بی‌نظیر است و تجربه کاربری روان‌تر و پایداری بهتر سرور را تضمین می‌کند.

2. استراتژی‌های مقاوم مدیریت خطا

انتشار خودکار رد شدن (Rejection) وعده‌ها در زنجیره‌های ناهمگام پیچیده می‌تواند چالش‌برانگیز باشد. یک نیاز رایج، مدیریت خطاها به صورت ظریف بدون توقف کل فرآیند است، مانند سناریوی "شکست نرم" که در آن می‌خواهید حتی اگر برخی از وظایف شکست بخورند، ادامه دهید.

ما می‌توانیم یک ابزار ایجاد کنیم که وعده‌ها را برای گرفتن خطاها به صورت جداگانه در حالی که نتایج موفق را حفظ می‌کند، در بر بگیرد:

function safeExecute(fn) {
  return fn().then(
    (data) => ({ success: true, data }),
    (error) => ({ success: false, error })
  );
}

async function processWithResilience(tasks) {
  // نگاشت هر وظیفه به یک بسته‌بندی اجرای ایمن
  const wrappedTasks = tasks.map(safeExecute);
  
  // اجرای همزمان تمام وظایف
  const results = await Promise.all(wrappedTasks);
  
  // جدا کردن موفقیت‌ها از شکست‌ها
  const successes = results.filter(r => r.success).map(r => r.data);
  const failures = results.filter(r => !r.success).map(r => r.error);
  
  return { successes, failures };
}

این رویکرد به توسعه‌دهندگان اجازه می‌دهد تا منطق تلاش مجدد (Retry logic)، حالت‌های رابط کاربری جایگزین (Fallback UI) یا مکانیسم‌های ثبت رویداد (Logging) را بدون کرش کردن برنامه پیاده‌سازی کنند.

3. ساخت تکرارپذیرهای ناهمگام سفارشی

ES2018 تکرارپذیرهای ناهمگام (Async iterables) را معرفی کرد که به ما امکان استفاده از حلقه‌های for await...of را می‌دهد. این مورد برای داده‌های جریان‌دار (Streaming) یا پردازش آرایه‌های بزرگ که استفاده از حافظه نگرانی‌برانگیز است، عالی است. به جای بارگذاری تمام داده‌ها در حافظه، می‌توانیم آن‌ها را تکه‌تکه پردازش کنیم.

async function* generateStream(items) {
  for (const item of items) {
    yield item;
    // شبیه‌سازی پردازش ناهمگام یا تأخیر
    await new Promise(resolve => setTimeout(resolve, 100));
  }
}

// استفاده
async function processLargeDataset() {
  const data = Array.from({ length: 1000 }, (_, i) => i);
  for await (const item of generateStream(data)) {
    console.log(`Processed: ${item}`);
  }
}

با بازگرداندن کنترل به حلقه رویداد (Event loop) پس از هر آیتم، از مسدود شدن رشته اصلی جلوگیری می‌کنیم که منجر به برنامه‌ای واکنش‌گراتر می‌شود، حتی تحت بار کاری سنگین.

نتیجه‌گیری

تسلط بر الگوهای ناهمگام پیشرفته، نحوه مدیریت پیچیدگی در برنامه‌های جاوااسکریپت را دگرگون می‌کند. با پیاده‌سازی کنترل همزمانی، از زیرساخت خود محافظت می‌کنید. با پذیرش مدیریت خطای مقاوم، پایداری را تضمین می‌کنید. و با بهره‌گیری از تکرارپذیرهای ناهمگام، عملکرد را بهینه می‌کنید. این تکنیک‌ها فقط مفاهیم نظری نیستند؛ آن‌ها ابزارهای ضروری برای ساخت برنامه‌های فرانت‌اند مقیاس‌پذیر، قابل نگهداری و با عملکرد بالا هستند. شروع به ادغام این الگوها در پروژه بعدی خود کنید تا کیفیت کد و جریان توسعه خود را ارتقا دهید.

Share: