JavaScript非同期プログラミングの4つの方法


非同期プログラミングは、JavaScriptを使用してプログラムを作成する人ごとに発生する問題であり、フロントエンドのajax要求、またはnodeの様々な非同期APIに関係なく.本論文では,一般的な四つの非同期プログラミングを扱う方法をまとめた.
コールバック関数
コールバック関数を使用するのは最も一般的な形式で、いくつかの例を挙げます.
// jQuery ajax
$.get('test.html', data => {
  $('#result').html(data)
})
// node       
const fs = require('fs')

fs.readFile('/etc/passwd', (err, data) => {
  if (err) {
    throw err
  }
  console.log(data)
})
コールバック関数は、関数を定義する際に別の関数(コールバック関数)をパラメータとして定義された関数に導入し、非同期動作が実行された後にこのコールバック関数を実行することで、次の動作が非同期動作の後に実行されることを確認することができます.
コールバック関数の欠点は、複数の非同期動作を実行すると複数のコールバック関数がネストされ、コード構造が乱れ、コールバック地獄と呼ばれる.
func1(data0, data1 => {
  func2(data2, data3 => {
    func3(data3, data4 => data4)
  })
})
Promise
Promiseは、チェーンで呼び出された方法で非同期コードを組織し、元のコールバック関数として呼び出されたコードをチェーン式呼出しに変更することができます.
// jQuery ajax promise   
$.get('test.html')
  .then(data => $(data))
  .then($data => $data.find('#link').val('href'))
  .then(href => console.log(href))
自分でプロモーション形式を定義する関数はES 6の中でも非常に簡単です.
function ready() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('ready')
    }, 3000)
  })
}

ready().then(ready => console.log(`${ready} go!`))
node 8.0以上のバージョンでは、util.promisify方法を用いて、コールバック形式の関数をPromise形式に変更することもできる.
const util = require('util')
const fs = require('fs')

const readPromise = util.promisify(fs.readFile)

readPromise('test.txt').then(data => console.log(data.toString()))
Promiseを詳しく知りたいですが、拙作を読んでES 6のPromiseオブジェクトについて話してもいいです.
Generators
nodeの有名な開発者TJは、ES 6の新特性生成器(Generators)を用いて、非同期制御ツールcoを開発した.
Generatorsを知らないなら、以下の文章を見てもいいです.
  • 深浅浅出ES 6(3):ジェネレータGenerators
  • 深入りES 6(11):ジェネレータGenerators、続編
  • coを利用して、同期コードのような書き方ができます.
    const util = require('util')
    const fs = require('fs')
    const co = require('co')
    
    const readFile = util.promisify(fs.readFile)
    
    co(function* () {
      const txt = yield readFile('file1.txt', 'utf8')
      console.log(txt)
      const txt2 = yield readFile('file2.txt', 'utf8')
      console.log(txt2)
    })
    Generatorsを使っているのは明らかに分かりやすいようで、非同期コードをはっきりと書くことができます.
    Async/Await
    node 7.6以上のバージョンはES 7の新しい特性を導入しました.Aync/Awaitは非同期コードを制御するために専用です.まず例を見ます.
    const util = require('util')
    const fs = require('fs')
    
    const readFile = util.promisify(fs.readFile)
    
    async function readFiles () {
      const txt = await readFile('file1.txt', 'utf8')
      console.log(txt)
      const txt2 = await readFile('file2.txt', 'utf8')
      console.log(txt2)
    })
    まず、asyncのキーワードを使用して、非同期コードを含む関数を定義し、Promise形式の非同期関数の前にawaitのキーワードを使用すれば、非同期を同期動作として書き上げることができる.
    Generators制御方式とは大きく違っているように見えますが、Aync/Awaitは元々非同期をコントロールするために使われていますので、おすすめです.
    エラー処理
    最後に、次の4つの非同期制御方法のエラー処理を検討する.
    コールバック関数
    コールバック関数のエラー処理はとても簡単です.コールバック関数でエラーメッセージを同時に送ることです.
    const fs = require('fs')
    
    fs.readFile('file.txt', (err, data) => {
      if (err) {
        throw err
      }
      console.log(data)
    })
    Promise
    Promiseは、then方法の後に、エラー情報をキャプチャするためにcatchスキームを使用する.
    const fs = require('fs')
    const readFile = util.promisify(fs.readFile)
    
    readFile('file.txt')
      .then(data => console.log(data))
      .catch(err => console.log(err))
    GeneratorsとAync/Await
    GeneratorsとAync/Awaitは比較的に類似しており、第一にPromiseを使用するcatchの方法があり、第二にtry catchのキーワードを使用する.
    Promise catch
    const fs = require('fs')
    const co = require('co')
    const readFile = util.promisify(fs.readFile)
    
    co(function* () {
      const data = yield readFile('file.txt').catch(err => console.log(err))
    })
    const fs = require('fs')
    const co = require('co')
    const readFile = util.promisify(fs.readFile)
    
    async function testRead() {
      const data = await readFile('file.txt').catch(err => console.log(err))
    }
    try/catch
    const fs = require('fs')
    const co = require('co')
    const readFile = util.promisify(fs.readFile)
    
    co(function* () {
      try {
        const data = yield readFile('file.txt')
      } catch(err) {
        console.log(err)
      }
    })
    const fs = require('fs')
    const readFile = util.promisify(fs.readFile)
    
    async function testRead() {
      try {
        const data = await readFile('file.txt')
      } catch(err) {
        console.log(data)
      }
    }
    読んでいただいてありがとうございます.足りないところがあれば指摘してください.
    参照
  • ES 6のPromiseオブジェクトについて話す
  • 深浅浅出ES 6(3):ジェネレータGenerators
  • 深入りES 6(11):ジェネレータGenerators、続編
  • この記事は私の個人ブログと同期します.http://blog.acwong.org/2017/06/24/javascript-async-programming/