Generator و yield: ساخت آسان Iterator با توابع مکث‌پذیر

در درس‌های قبل دیدید که ساخت یک Iterator سفارشی، حتی برای یک ساختار ساده، نیازمند نوشتن دستی متد next و مدیریت وضعیت داخلی مثل شمارنده‌ی index بود. Generator ابزاری در جاوااسکریپت است که دقیقاً همین کار را بسیار ساده‌تر می‌کند: نوعی تابع خاص که می‌تواند اجرای خود را در میانه‌ی راه متوقف کند، یک مقدار برگرداند، و بعداً دقیقاً از همان نقطه ادامه دهد.

تعریف یک تابع Generator

تابع Generator با قرار دادن یک ستاره (*) بعد از کلیدواژه‌ی function تعریف می‌شود. فراخوانی این تابع، برخلاف توابع معمولی، بلافاصله بدنه‌ی آن را اجرا نمی‌کند؛ بلکه یک شیء Generator که خودش هم Iterable و هم Iterator است برمی‌گرداند:

function* simpleGenerator() {
  console.log("این خط هنوز اجرا نشده");
}

const gen = simpleGenerator();
console.log(gen); // یک شیء Generator، بدون اینکه چیزی چاپ شده باشد

کلیدواژه‌ی yield

درون یک تابع Generator، کلیدواژه‌ی yield اجرای تابع را متوقف کرده و مقداری را به بیرون می‌فرستد. با هر فراخوانی متد next() روی شیء Generator، اجرا از همان نقطه‌ای که متوقف شده بود، ادامه پیدا می‌کند:

اجرای گام‌به‌گام یک Generator
خروجی کنسول اینجا نمایش داده می‌شود...

این رفتار دقیقاً همان ساختار { value, done } است که در درس Iterator دیدید؛ به همین دلیل شیء Generator به‌طور خودکار پروتکل Iterator را برآورده می‌کند و نیازی به نوشتن دستی متد next نیست.

Generator به‌عنوان Iterable در for...of

از آنجا که Generator هم Iterable است، مستقیماً در for...of قابل استفاده است، بدون نیاز به فراخوانی دستی next():

function* colors() {
  yield "قرمز";
  yield "سبز";
  yield "آبی";
}

for (const color of colors()) {
  console.log(color);
}
// قرمز
// سبز
// آبی

بازنویسی مثال بازه‌ی عددی با Generator

در درس Iterator، یک بازه‌ی عددی سفارشی با نوشتن دستی متد next ساخته شد. همان قابلیت با Generator بسیار کوتاه‌تر و خواناتر پیاده‌سازی می‌شود:

function* range(from, to) {
  for (let i = from; i <= to; i++) {
    yield i;
  }
}

for (const num of range(1, 5)) {
  console.log(num); // 1, 2, 3, 4, 5
}

جدول مقایسه‌ی Iterator دستی و Generator

ویژگی Iterator دستی Generator
نحوه‌ی تعریف پیاده‌سازی صریح متد next و مدیریت وضعیت تابعی با function* و کلیدواژه‌ی yield
حجم کد بیشتر و پرجزئیات‌تر کوتاه‌تر و خواناتر
مدیریت وضعیت داخلی دستی، با متغیرهایی مثل index خودکار، توسط موتور جاوااسکریپت

نکات کلیدی

  • تابع Generator با function* تعریف می‌شود و فراخوانی آن، برخلاف تابع معمولی، بدنه را بلافاصله اجرا نمی‌کند.
  • کلیدواژه‌ی yield اجرا را متوقف کرده و یک مقدار برمی‌گرداند؛ فراخوانی بعدی next اجرا را از همان نقطه ادامه می‌دهد.
  • شیء Generator به‌طور خودکار هم Iterable و هم Iterator است و مستقیماً در for...of قابل استفاده است.
  • Generator جایگزینی بسیار ساده‌تر برای نوشتن دستی Iterator با متد next است.

جمع‌بندی

Generator با ترکیب function* و yield، نوشتن Iterator سفارشی را از یک فرآیند چندخطی و دستی، به چند خط ساده و خوانا تبدیل می‌کند. اجرای مکث‌پذیر این توابع، امکان تولید مقادیر را به‌صورت گام‌به‌گام و حتی برای دنباله‌های نامتناهی فراهم می‌کند که در درس بعدی با کاربردهای پیشرفته‌تر آن آشنا خواهید شد.