بهرنگ نوروزی نیا درباره بایگانی

آشنایی با آزانگر (Generator) در جاوا اسکریپت - بخشِ یک

تازه ترین نسخه ی جاوا اسکریپت (ES6) یک ویژگی به نامِ «آزانگر» (Generator) دارد که در این نوشته می کوشم شما را با آن آشنا کنم.

آزانگر؟

شاید بپرسید که آزانگر چیست و آن را از کجا در آورده ام و چرا نمی گویم «تولید کننده» یا «سازنده» یا «بوجود آورنده» یا «آفریننده» یا «generate کننده» یا چیز های دیگر.

آزانگر را از واژه نامه ی ریشه شناختی اختر شناسی-اختر فیزیک حیدری ملایری آورده ام که واژه نامه ی بسیار دقیقی است و این زمانِ خوبی برای آوردنِ این واژه است، چون این ویژگی تازه به زبان افزوده شده و می توان در همین آغازِ کار واژه ی درست و بجایی را برای آن برگزید، پیش از آن که واژه های نادرستِ دیگر به جای آن به کار برده شوند.

آزانگر چیست؟

آزانگر تابعی است که می توان در میانه ی اجرای آن از آن بیرون آمد و سپس دوباره از همان جا اجرا را ادامه داد.

ولی این توضیح ارزش آن را نشان نمی دهد. با یک نمونه کد بهتر می شود آن را فهمید.

نمونه کدِ ساده

کدِ زیر را ببینید تا آن را با هم بررسی کنیم.

function* sample() {
  yield 1
  yield 2
  return 3
}

var gen = sample()

console.log(gen.next())
console.log(gen.next())
console.log(gen.next())

// output:
// { value: 1, done: false }
// { value: 2, done: false }
// { value: 3, done: true }

نخستین چیزی که تازه است، function* است که یک آزانگر را تعریف می کند. در همین آزانگر، yield را نیز می بینید که تنها می توان آن را در آزانگر ها نوشت. در خطِ ۱ تا ۵ آزانگرِ sample را تعریف کرده ایم. در خطِ ۷ آن را فراخوانده ایم که object ای باز می گرداند که آن را gen نامیده ایم. سپس ۳ بار next() را روی آن فراخوانی کرده ایم.

در بارِ نخست که next() فراخوانده شده، آزانگرِ sample اجرا شده و تا yield 1 پیش می رود. سپس 1 باز می گردد و تابع در همان جا می ایستد. با فراخوانیِ دوباره ی next() تابع کار را ادامه می دهد تا به yield 2 می رسد. سپس همان جا می ایستد و 2 را باز می گرداند. در بارِ سوم اجرای تابع ادامه می یابد تا به return 3 می رسد. در اینجا کارِ آزانگر پایان می یابد و 3 را باز می گرداند.

نتیجه در پایان آورده شده است. هر بار که next فراخوانده شده، یک object باز گشته که ۲ کلید دارد: value که مقدار باز گشته از آزانگر را در خود دارد و done که می گوید آیا کارِ آزانگر پایان یافته یا نه.

ویرایش: تابعی که با function* تعریف می شود، تابعِ آزانگر یا GeneratorFunction نامیده می شود و object ای که با فراخوانیِ آن باز می گردد یک Generator Object است.

function* genFun() {}
console.log(genFun.constructor.name) // "GeneratorFunction"

var genObj = genFun()
console.log(genObj.toString()) // [object Generator]

چگونه آزانگر ها را اجرا کنید

Firefox در نسخه ی ۲۶ و پس از آن، از آزانگر ها پشتیبانی می کند. می توانید نمونه کدِ بالا را در console ِ آن بنویسید تا نتیجه را ببینید.

Chrome در نسخه ی ۲۹ و پس از آن، از آزانگر ها پشتیبانی می کند ولی باید یک flag را فعال کنید تا بتوانید آزانگر ها را اجرا کنید. برای این کار، به about:flags یا chrome://flags/ بروید و Enable Experimental JavaScript را فعال کنید. سپس Chrome را یک بار ببندید و دوباره باز کنید. اکنون می توانید نمونه کد را اجرا کنید.

Node.js هم در آخرین نسخه ی خود که هنوز پایدار نشده (0.11+) از آزانگر ها پشتیبانی می کند ولی برای فعال کردنِ آن ها باید node را با --harmony-generators یا --harmony اجرا کنید:

$ node --version
v0.11.12
$ node --harmony-generators sample.js
{ value: 1, done: false }
{ value: 2, done: false }
{ value: 3, done: true }

نمونه کدِ پیشرفته تر

یک نمونه کدِ بهتر و کاربردی تر در زیر آمده است.

function* fibonacci() {
  var fn1 = 1
    , fn2 = 1
    , current
  while (true) {
    current = fn2
    fn2 = fn1
    fn1 = fn1 + current
    yield current
  }
}

var sequence = fibonacci()

function logNext() {
  console.log(sequence.next().value)
  setTimeout(logNext, 1000)
}

logNext()

این آزانگرِ بی پایانِ فیبوناچی است که هر بار یک عدد را در دنباله ی فیبوناچی می آزاند. این بار آزانگر در یک چرخه ی بی پایان است. برای اجرای آن هم هر یک ثانیه یک بار next را روی آن فراخوانده ایم.

همین؟

شاید با خود بگوییم که جالب بود ولی نیازی به دنباله ی فیبوناچی یا این جور چیز ها نداریم که بخواهیم آزانگر ها را به کار بریم. آزانگر ها خیلی بیشتر از این اند و جاوا اسکریپت را در بسیاری از زمینه ها دگرگون خواهند کرد. توانایی نگه داشتن یک تابع در میانه ی اجرا و سپس ادامه ی آن، توانایی بسیار پر کاربردی است. این نوشته تنها برای آشنایی بود. در بخش دوم کمی با کاربرد های پیشرفته ترِ آزانگر ها آشنا خواهیم شد.