はじめに
この記事ではJavaScriptにおける非同期処理の取り扱いを紹介します。
第0世代: sync
fs
のreadFileSync
のように一部のライブラリでは、非同期関数に加えて同期関数が用意されています。ビルドスクリプトのように高い処理性能が要求されない場面では、あえて非同期にしないというやり方も現実的な選択肢だと考えます。
メリット
- 自然な記述が可能
デメリット
- ブロックが発生する
例
'use strict';
var fs = require('fs')
var buffer = fs.readFileSync(__filename)
var text = buffer.toString()
console.log(text)
第1世代: callback
関数の呼び出し時に引数としてコールバック関数を指定します。コールバック関数は、処理終了時やエラー発生時に呼び出されます。現在のNode.jsにおける標準的な非同期の取り扱い方法です。
メリット
- とってもシンプル
- 大体いつでも使える(Node.js & ブラウザ)
デメリット
- 非同期処理が続くとインデントが深くなる
- 複数の非同期処理を扱うには工夫が必要
例
'use strict';
var fs = require('fs')
var buffer = fs.readFile(__filename, function (err, buffer) {
if (err) {
console.error(err.stack)
}
var text = buffer.toString()
console.log(text)
})
第2世代: promise
Promise
というオブジェクトを生成し、then
やcatch
などのメソッドをつなげて記述するのが特徴です。深いインデントの解消や複数の非同期処理の並列実行が可能になる点などcallbackのデメリットを補うことができます。
メリット
- 深いインデントの解消
-
Promise.all
関数により複数の非同期処理の並列実行が可能
デメリット
- コードを書く量が増える
- Promiseを知らないと「?」となる
- ブラウザでは
Q
やbluebird
などの外部ライブラリが必要
例
'use strict';
var fs = require('fs')
new Promise(function (resolve, reject) {
fs.readFile(__filename, function (err, buffer) {
err ? reject(err) : resolve(buffer)
})
})
.then(function (buffer) {
var text = buffer.toString()
console.log(text)
})
.catch(function (err) {
console.error(err.stack)
})
第3世代: yield
ES6のyield
キーワードとco
というライブラリによって実現されます。syncのわかりやすさとpromise並の細やかな制御が可能な点を兼ね備えています。
メリット
- 自然な記述
- promise並の細やかな制御が可能
デメリット
- co(function *() {})
でくくる必要がある
- 知らない方にはpromise
以上に「?」となる
- 基本的にブラウザでは使用できない(regenerator
を使うなど方法は有)
例
'use strict';
var fs = require('fs')
var co = require('co')
co(function *() {
var buffer = new Promise(function (resolve, reject) {
fs.readFile(__filename, function (err, buffer) {
err ? reject(err) : resolve(buffer)
})
})
var text = buffer.toString()
console.log(text)
})
.catch(function (err) {
console.error(err.stack)
})
co + Qが現状では使い勝手が良い
Q
というライブラリのQ.nfcall
という関数を使用して、記述量をさらに減らすことができます。
'use strict';
var fs = require('fs')
var Q = require('q')
var co = require('co')
co(function *() {
var buffer = Q.nfcall(fs.readFile, __filename)
var text = buffer.toString()
console.log(text)
})
.catch(function (err) {
console.error(err.stack)
})
おわりに
状況に合わせて適切な非同期の取り扱いを!