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

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

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

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

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

بیایید نخست یک برنامه ی نمونه که با express نوشته شده را ببینیم و سپس بکوشیم آن را با koa بنویسیم. برنامه ی نمونه ی ما خیلی ساده است. کاری که می کند این است که اگر به ریشه (/) برویم، فهرستی از پرونده های markdown که در پوشه ی md/ هستند را نشان می دهد و اگر روی هر کدام از آن ها کلیک کنیم، آن پرونده ی markdown را به html تبدیل کرده و نمایش می دهد. کدِ آن را در زیر ببینید.

var fs = require('fs')
  , marked = require('marked')
  , express = require('express')
  , app = express()
  , port = 3000

app.get('/', list)
app.get('/:filename', show)

app.listen(port, function () {
  console.log('listening on port %s', port)
})

function list(req, res, next) {
  fs.readdir('md/', function (err, files) {
    if (err) return next(err)
    res.send(renderList(files))
  })
}

function show(req, res, next) {
  var filename = req.params.filename
  fs.readFile('md/' + filename, 'utf-8', function (err, content) {
    if (err) return next(err)
    res.send(marked(content))
  })
}

function renderList(filenames) {
  var links = filenames.map(function (filename) {
                return '* [' + filename + '](' + filename + ')'
              })
  return marked('#Markdown files\n' + links.join('\n'))
}

برنامه ی بسیار ساده ای است. app یک برنامه ی express است که به درگاهِ 3000 گوش می کند. اگر به / برویم، تابعِ list فراخوانی می شود که فهرست پرونده های پوشه ی md را به دست می آورد و سپس نتیجه ی renderList را نشان می دهد. renderList فهرست پرونده ها را تبدیل به یک پرونده ی markdown می کند که پیوند هایی به هر پرونده دارد و سپس آن را تبدیل به html می کند.

اگر به /:filename برویم (که filename در اینجا نام هر پرونده ی پوشه ی md خواهد بود)، تابعِ show فراخوانی می شود که پرونده را خوانده و آن را تبدیل به html می کند و نمایش می دهد.

koa چیست؟

سازنده ی express همان TJ Holowaychuk است که پس از نوشتنِ co و thunkify برای به کار گیریِ آزانگر ها در نوشتنِ وب سرور ها، koa را ساخت. koa به سادگیِ express است ولی با داشتنِ خوبی های آزانگر ها.

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

اکنون همان برنامه را با koa می نویسیم. فراموش نکنید که برای اجرای آن به نسخه ی v0.11.x از node نیاز دارید و باید --harmony را به آن بفرستید. کدِ زیر را ببینید.

var fs = require('fs')
  , marked = require('marked')
  , koa = require('koa')
  , app = koa()
  , port = 3000
  , route = require('koa-route')
  , thunkify = require('thunkify')
  , readDir = thunkify(fs.readdir)
  , readFile = thunkify(fs.readFile)

app.use(route.get('/', list))
app.use(route.get('/:filename', show))

app.listen(port, function () {
  console.log('listening on port %s', port)
})

function* list() {
  var files = yield readDir('md/')
  this.body = renderList(files)
}

function* show(filename) {
  var content = yield readFile('md/' + filename, 'utf-8')
  this.body = marked(content)
}

function renderList(filenames) {
  var links = filenames.map(function (filename) {
                return '* [' + filename + '](' + filename + ')'
              })
  return marked('#Markdown files\n' + links.join('\n'))
}

به جای express در اینجا koa را آورده ایم. koa-route کاری همانند route ها در express می کند که در koa نیست و باید آن را جداگانه بیاوریم. marked و thunkify و readDir و readFile هم در بخش پیش گفته شدند.

‍تابعِ renderList همانند پیش است. route ها اندکی تغییر کرده اند ولی مشخص است که چه می کنند. بیشترین تغییر را list و show دارند. این دو دیگر تابع نیستند، تابعِ آزانگر هستند.

list یک تابعِ آزانگر است که نخست فهرستِ پرونده های پوشه ی md را به دست می آورد. سپس آن ها را به renderList می دهد و نتیجه را در this.body می گذارد. در koa دیگر از پارامتر های req و res و next خبری نیست. برای دسترسی به request و response باید با context کار کنید که برای دسترسی به آن this را به کار می برید. در اینجا برای نوشتنِ نتیجه تنها باید آن را در this.body بگذاریم. با این کار koa نتیجه را به درخواست کننده باز می گرداند.

show هم یک تابعِ آزانگر شده که یک پارامتر به نامِ filename می گیرد. koa-route این پارامتر را از نشانیِ درخواست گرفته و به show می فرستد. هر بار که اجرا شود، پرونده را می خواند و آن را تبدیل به html می کند و سپس نتیجه را در this.body می گذارد که koa آن را به درخواست کننده باز می گرداند.

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

نمونه کدی که با koa نوشته شده دو ویژگیِ خیلی مهم در برابرِ express دارد. ویژگیِ نخست گرفتنِ خطا ها است. همان گونه که در بخش های پیش گفته شد، گرفتنِ خطا در اینجا خیلی ساده است و با try/catch به سادگی می توان خطا را کنترل کرد.

ویژگی دوم هم این است که کد خیلی خوانا تر شده. list یا show را که کارِ اصلیِ برنامه را انجام می دهند در هر دو نمونه کد با هم مقایسه کنید. در نمونه کدِ دوم روندِ برنامه مشخص است و با یک نگاه خوانده می شود. ولی در نمونه کدِ یک خیلی پیچیده تر است. توجه کنید که این یک نمونه کدِ خیلی ساده است و در برنامه های کاربردی، کد ها خیلی تو در تو تر از این هستند، که خوانایی برنامه را کمتر و کمتر می کند.

دیدگاهِ شما چیست؟

آیا آزانگر ها و koa پیچیده هستند؟ آیا callback ها و express ناخوانا تر هستند؟