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

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

پیش از خواندنِ این نوشته، بخشِ یکم و بخشِ دومِ آن را بخوانید.

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

کنترلِ روندِ موازی

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

var fs = require('fs')

function readFile(path, done) {
  fs.readFile(path, 'utf-8', done)
}

function readFiles(paths, done) {
  var count = paths.length
    , result = new Array(count)
    , doneCalled = false

  paths.map(function (path, i) {
    readFile(path, function (err, data) {
      if (doneCalled) return
      if (err) {
        doneCalled = true
        done(err)
        return
      }

      result[i] = data

      count -= 1
      if (count === 0)
        done(null, result)
    })
  })
}

readFiles(['md/file1.md', 'md/file2.md', 'md/file3.md'], function (err, res) {
  if (err) return console.error(err.message)
  res.map(function (data) {
    console.log(data)
  })
})

همان گونه که می بینید، کار کمی پیچیده تر شده است. تابعِ map که روی آرایه ها تعریف شده، تابعی که به آن فرستاده می شود را روی همه ی داده های آرایه اجرا می کند. از آنجایی که پاسخِ خواندنِ پرونده ها به ترتیب نخواهد بود، برای هر پاسخ یکی از count کم می کنیم تا به صفر برسد. سپس اگر صفر شد، done را فرا می خوانیم. پاسخ های باز گشته را هم در آرایه ی result در سر جای خود گذاشته ایم.

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

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

به کار گیریِ co

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

var fs = require('fs')
  , co = require('co')

function readFile(path) {
  return function (done) {
    fs.readFile(path, 'utf-8', done)
  }
}

function readFiles(paths, done) {
  co(function* () {

    return yield paths.map(readFile)

  })(done)
}

readFiles(['md/file1.md', 'md/file2.md', 'md/file3.md'], function (err, res) {
  if (err) return console.error(err.message)
  res.map(function (data) {
    console.log(data)
  })
})

کد بسیار ساده شده است. در اینجا paths.map(readFile) تابعِ readFile را روی تک تکِ اندیس های آرایه فرا می خواند که این تابع خود یک تابع دیگر باز می گرداند و paths.map همه ی آن ها را در یک آرایه ی دیگر می ریزد و باز می گرداند. پس با اجرای آن، به یک آرایه از تابع ها می رسیم که این تابع ها از readFile باز گشته اند.

سپس این آرایه به yield می رسد که آن هم این آرایه را می آزاند و co آن را می گیرد. co هر گاه که به یک آرایه یا یک object از تابع ها برسد، آن ها را موازی اجرا می کند و در پایانِ اجرای همه ی آن ها، نتیجه را به درونِ تابعِ آزانگر باز می گرداند و جایگزینِ yield می کند یا اگر خطایی رخ دهد، همان خطا را در جای yield پرتاب می کند. پس اگر خطایی رخ ندهد، به یک آرایه از داده های درونِ پرونده ها می رسیم. سپس return نیز آن را باز می گرداند. co این آرایه ی باز گشته را می گیرد و آن را به تابعِ done که به آن فرستاده شده می فرستد. اگر خطایی رخ دهد که catch نشده باشد، done را با آن فرا می خواند.

می بینید که کار با co خیلی ساده است و چه اندازه کد را ساده تر کرده است. می توانید بگویید که کتابخانه های دیگری هم هستند که کدِ نمونه ی نخست را ساده تر می کنند (مانند async) ولی باز در کنترلِ خطا به co نمی رسند.

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

thunk چیست؟

به تابعی که بیشتر خودکار ساخته می شود تا به فراخوانیِ یک تابعِ دیگر کمک کند، thunk می گویند. بیشتر برای این به کار می روند که کمی فراخوانیِ تابعِ دیگر را ساده تر کنند.

یک نمونه از آن را کمی بالا تر دیدیم. در اینجا دوباره آورده شده است.

function readFile(path) {
  return function (done) {
    fs.readFile(path, 'utf-8', done)
  }
}

در اینجا readFile یک thunk است که تنها فراخوانیِ fs.readFile را برای ما ساده کرده است. بیشتر تابع ها در Node.js یک callback می گیرند که اگر فراخوانده شود، دو پارامتر دارد: (error, response). ولی co برای اینکه بتواند کار کند، نیاز دارد تا تابعی برای آن آزانده شود که تنها یک callback می گیرد که همین دو پارامتر را می گیرد و نه چیزِ دیگری. برای همین readFile را نوشتیم تا بتوانیم آن را به جای fs.readFile به co بفرستیم.

با thunkify آشنا شوید

thunkify یک کتابخانه یِ دیگر است که آن هم بدستِ TJ Holowaychuk نوشته شده و کارِ بسیار ساده ای می کند. با آن می توانید به سادگی thunk برای تابع های Node.js که callback می گیرند بسازید. با به کار گیری آن، دیگر نیازی به نوشتنِ تابعِ کدِ پیش نیست.

var readFile = thunkify(fs.readFile)

این همان کارِ کدِ پیش را می کند.

برنامه ی نمونه

بیایید یک نمونه کدِ پیشرفته تر را ببینیم. بیایید بگوییم می خواهیم همه ی پرونده های با غالبِ markdown را که در پوشه ی md/ هستند به html تبدیل کنیم و آن ها را در پوشه ی html/ بریزیم. کدِ زیر این کار را با callback ها انجام می دهد.

var fs = require('fs')
  , marked = require('marked')

function convert(inDir, outDir, done) {
  fs.readdir(inDir, function (err, files) {
    if (err) return done(err)

    var count = files.length
      , doneCalled = false
      , htmlData = new Array(count)

    files.map(function (file, i) {
      fs.readFile(inDir + file, 'utf-8', function (err, data) {
        if (doneCalled) return
        if (err) {
          doneCalled = true
          done(err)
          return
        }

        htmlData[i] = marked(data)
        fs.writeFile(outDir + file + '.html', htmlData[i], function (err) {
          if (doneCalled) return
          if (err) {
            doneCalled = true
            done(err)
            return
          }

          count -= 1
          if (count === 0) done(null, htmlData)
        })
      })
    })
  })
}

convert('md/', 'html/', function (err, res) {
  if (err) return console.error(err.message)
  console.log('%d files converted', res.length)
})

در اینجا marked کارِ تبدیلِ markdown به html را انجام می دهد. با fs.readdir پرونده های درونِ پوشه ی md/ را خوانده ایم. سپس همانندِ پیش برای آن که بتوانیم پرونده ها را موازی بخوانیم، چند متغیر گرفته ایم. پرونده ها را با fs.readFile خوانده ایم. کنترلِ خطا کرده ایم. سپس هر پرونده را به html تبدیل کرده ایم و پس از آن هم آن ها را در پوشه ی html/ ذخیره کرده ایم و باز هم در پایان کنترلِ خطا کرده ایم و سر انجام done را فراخوانده ایم.

اکنون همین کار را با co و thunkify انجام می دهیم.

var fs = require('fs')
  , marked = require('marked')
  , co = require('co')
  , thunkify = require('thunkify')
  , readDir = thunkify(fs.readdir)
  , readFile = thunkify(fs.readFile)
  , writeFile = thunkify(fs.writeFile)

function convert(inDir, outDir, done) {
  co(function* () {
    var files, filesData, htmlData

    files = yield readDir(inDir)

    filesData = yield files.map(function (file) {
      return readFile(inDir + file, 'utf-8')
    })

    htmlData = filesData.map(function (data) { return marked(data)})

    yield files.map(function (file, i) {
      return writeFile(outDir + file + '.html', htmlData[i])
    })

    return htmlData

  })(done)
}

convert('md/', 'html/', function (err, res) {
  if (err) return console.error(err.message)
  console.log('%d files converted', res.length)
})

می بینید که در آغاز thunk های readDir و readFile و writeFile را ساخته ایم. سپس در تابعِ آزانگری که به co فرستاده شده، نخست فهرست پرونده های درونِ پوشه ی md/ را خوانده ایم. سپس داده های پرونده ها را به دست آورده ایم. سپس همه را به html تبدیل کرده ایم و پس از آن همه را در پوشه ی html/ ذخیره کرده ایم. سرانجام هم html ها را باز گردانده ایم که co آن را به done می دهد. اگر خطایی هم رخ دهد، آن را به done می دهد.

این دو نمونه ی بالا را با هم مقایسه کنید. نمونه کدِ با callback خیلی زود نا خوانا می شود و کنترلِ خطا هم در آن خیلی دشوار تر است و شاید خیلی ساده چیزی را در آن میان فراموش کنید. دیگر کاری که برنامه می کند به سادگی فهمیده نمی شود.

کدی که با آزانگر نوشته شده، خیلی سر راست تر، ساده تر و فهمیدنی تر است - اگر آزانگر ها را بدانید. روندِ برنامه مشخص است و کنترلِ خطا هم ساده است. اگر بخواهید می توانید درونِ همین تابعِ آزانگر try/catch بگذارید و خطاهایی که درونِ هر یک از thunk ها رخ دهد را بگیرید و کاری برای آن بکنید.

سر انجام

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

اگر از این نوشته خوش تان آمد، می توانید آن را به دنبال کنندگان تان در تویتر بشناسانید یا اینکه مرا در تویتر دنبال کنید.

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

پیش از خواندنِ این نوشته، بخشِ نخستِ آن را بخوانید.

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

کنترلِ روندِ اجرا چیست؟

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

var fs = require('fs')

function readFile(path, done) {
  fs.readFile(path, 'utf-8', done)
}

function readTwoFiles(file1, file2, done) {
  readFile(file1, function (err, data1) {
    readFile(file2, function (err, data2) {
      done(null, [data1, data2])
    })
  })
}

readTwoFiles('md/file1.md', 'md/file2.md', function (err, res) {
  console.log(res[0])
  console.log(res[1])
})

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

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

به کار گیریِ آزانگر

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

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

اکنون می توانیم بخشی از کد را بنویسیم.

    ...
    var data1 = yield readFile(file1)
    var data2 = yield readFile(file2)
    done(null, [data1, data2])
    ...

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

var fs = require('fs')
  , run = require('./run')

function readFile(path) {
  return function (done) {
    fs.readFile(path, 'utf-8', done)
  }
}

function readTwoFiles(file1, file2, done) {
  run(function* () {
    var data1 = yield readFile(file1)
    var data2 = yield readFile(file2)
    done(null, [data1, data2])
  })
}

readTwoFiles('md/file1.md', 'md/file2.md', function (err, res) {
  console.log(res[0])
  console.log(res[1])
})

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

module.exports = run

function run(fn) {
  var gen = fn()

  function next(err, res) {
    var ret = gen.next(res)
    if (ret.done) return
    ret.value(next)
  }

  next()
}

به همین سادگی و کوتاهی. تابعِ run یک تابعِ آزانگر می گیرد. آزانگرِ آن را می سازد. یک تابعِ درونی به نامِ next تعریف می کند که کارِ اجرا را انجام می دهد. سپس آن را فرا می خواند. در درونِ next آزانگر اجرا می شود. اگر کارِ آزانگر پایان یافته باشد که هیچ. اگر نه، باید یک تابع را آزانیده باشد (value باید یک تابع باشد). آن را فرا می خواند و خود را callback ِ آن می کند. این کار را ادامه می دهد و هر بار که به callback چیزی باز گشته باشد (res)، آن را به آزانگر می دهد تا کار را ادامه دهد.

کنترلِ خطا

شاید با خود بگویید که خیلی پیچیده شد و کد چندان هم بهتر نشده. نمونه های بالا خیلی ساده شده بودند تا بتوانم کنترلِ روند را توضیح بدهم. ولی در کد هایی که در برنامه ها می نویسیم، کار به این سادگی نیست. چیزِ مهمی که در این جا ندیدیم، گرفتنِ خطا ها بود. گرفتنِ خطا ها در جاوا اسکریپت و برنامه های Node.js کارِ بسیار دشواری است. در کدِ زیر، خطاهای نمونه کدِ با callback کنترل شده اند.

var fs = require('fs')

function readFile(path, done) {
  fs.readFile(path, 'utf-8', done)
}

function readTwoFiles(file1, file2, done) {
  readFile(file1, function (err, data1) {
    if (err) return done(err)
    readFile(file2, function(err, data2) {
      if (err) return done(err)
      done(null, [data1, data2])
    })
  })
}

readTwoFiles('md/file1.md', 'md/file2.md', function (err, res) {
  if (err) return console.error(err.message)
  console.log(res[0])
  console.log(res[1])
})

می بینید که درونِ هر callback یک بار بررسی کرده ایم که آیا خطایی پیش آمده و یا نه. اگر خطا پیش آمده، done را با آن فرا می خوانیم. این کار را خیلی دشوار می کند. برنامه هایی که می نویسیم، همیشه خیلی پیچیده تر از این نمونه کدِ ساده هستند. زبان های برنامه نویسی، try/catch را برای ساده سازیِ همین مشکل آورده اند ولی در callback های جاوا اسکریپت، نمی توانیم آن ها را به کار بریم. چون کتاب خانه های درونیِ Node.js و دیگر کتاب خانه ها، تابعِ callback را درونِ یک try/catch فرا نمی خوانند.

اکنون بیایید کنترلِ خطا ها را با آزانگر ها انجام دهیم. چیزِ دیگری که در بخشِ نخست نگفتم، این است که به جای فراخوانیِ next() می توانید throw() را فرا بخوانید تا یک خطا در جایی از کد که yield بوده، throw شود. کدِ زیر را ببینید.

var fs = require('fs')
  , run = require('./run')

function readFile(path) {
  return function (done) {
    fs.readFile(path, 'utf-8', done)
  }
}

function readTwoFiles(file1, file2, done) {
  run(function* () {
    try {
      var data1 = yield readFile(file1)
      var data2 = yield readFile(file2)
      done(null, [data1, data2])
    } catch (err) {
      done(err)
    }
  })
}

readTwoFiles('md/file1.md', 'md/file2.md', function (err, res) {
  if (err) return console.error(err.message)
  console.log(res[0])
  console.log(res[1])
})

در اینجا خیلی ساده یک try/catch گذاشته ایم تا خطا ها را بگیریم. خیلی ساده تر از کدِ نخست است. کدِ run را هم ببینید.

module.exports = run

function run(fn) {
  var gen = fn()

  function next(err, res) {
    if (err) return gen.throw(err)
    var ret = gen.next(res)
    if (ret.done) return
    ret.value(next)
  }

  next()
}

تنها یک خط افزوده شده که اگر خطایی رخ داده باشد، throw(err) خطا را در درونِ آزانگر throw می کند.

چه به دست آوردیم؟

تا اینجا دیدید که آزانگر ها در کنترلِ روندِ اجرا به ما خیلی کمک می کنند. این که می توانیم در میانه ی اجرا تابع را نگه داریم، کاری را انجام دهیم، سپس کار را از همان جا ادامه دهیم یا اینکه خطایی به جای آن throw کنیم، بسیار بسیار ویژگیِ توانمندی است.

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

اگر از این نوشته خوش تان آمد، می توانید آن را به دنبال کنندگان تان در تویتر بشناسانید یا اینکه مرا در تویتر دنبال کنید.

آشنایی با آزانگر (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 را روی آن فراخوانده ایم.

همین؟

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

اگر از این نوشته خوش تان آمد، می توانید آن را به دنبال کنندگان تان در تویتر بشناسانید یا اینکه مرا در تویتر دنبال کنید.

خیار

این ارائه در ۲۱ اسفند ۹۱ انجام شده ولی خواستم پیش از نوشته های تازه تر آن را در اینجا بگذارم.

ابزار ها و روش های فراوانی برای آزمودنِ فراورده های نرم افزاری هستند. یکی از روش ها، Acceptance Test است و یکی از ابزارهای این روش «خیار» است. این ابزار کمک می کند تا نیازی که از فراورده دارید را به زبانِ آدمیزاد بنویسید که با این کار همه می توانند آن را بخوانند؛ هم برنامه نویس ها، هم مشتری. پس از آن که هر دو گروه آن را پذیرفتند، برنامه نویس ها می توانند کدی را روی آن بنویسند که آن را اجرا کند و درستیِ برنامه را بررسی کند.

اگر از این نوشته خوش تان آمد، می توانید آن را به دنبال کنندگان تان در تویتر بشناسانید یا اینکه مرا در تویتر دنبال کنید.

ارائه ی Node.js

این فیلم در ۱۶ شهریور ۹۰ گرفته شده ولی خواستم پیش از نوشته های تازه تر آن را در اینجا بگذارم.

امروزه دیگر Nodejs یک ابزارِ شناخته شده است و در همه جا دیده می شود. سالِ ۹۰ فناوریِ بسیار نوینی بود و در این فیلم کوشیدم که آن را آموزش دهم. برای این ارائه خیلی زمان گذاشته بودم. هنوز هم باید برای بینندگان چیزهای تازه و با ارزشی داشته باشد.

اگر از این نوشته خوش تان آمد، می توانید آن را به دنبال کنندگان تان در تویتر بشناسانید یا اینکه مرا در تویتر دنبال کنید.