JavaScript 非同期プログラミングについてもう一度話しましょう_JavaScript スキル

WBOY
リリース: 2016-05-16 15:17:35
オリジナル
1165 人が閲覧しました

フロントエンドの発展に伴い、非同期という言葉がますます一般的になってきました。次のような非同期タスクがあるとします。

サーバーに対していくつかのリクエストを開始し、各リクエストの結果が次のリクエストのパラメータとして使用されます。
何をしなければならないかを見てみましょう:

コールバック

最初に思い浮かび、最も一般的に使用されるのは、単純なカプセル化を作成してみましょう。

let makeAjaxCall = (url, cb) => {
  // do some ajax
  // callback with result
}

makeAjaxCall('http://url1', (result) => {
  result = JSON.parse(result)
})

ログイン後にコピー

うーん、なかなかいい感じですね!しかし、複数のタスクをネストしようとすると、コードは次のようになります:

makeAjaxCall('http://url1', (result) => {
  result = JSON.parse(result)

  makeAjaxCall(`http://url2?q=${result.query}`, (result) => {
    result = JSON.parse(result)

    makeAjaxCall(`http://url3?q=${result.query}`, (result) => {
      // ...
    })
  })
})

ログイン後にコピー

なんと!その山を地獄に落ちさせましょう })

そこで、JavaScript イベント モデル を使用してみます。

1. パブリッシュ/サブスクライブ

DOM イベントの処理では、Pub/Sub は非常に一般的なメカニズムです。たとえば、要素にイベント監視を追加する必要があります。

elem.addEventListener(type, (evt) => {
  // handler
})
ログイン後にコピー
では、非同期タスクを処理するために同様のモデルを構築できるでしょうか?

最初に、配送センターを構築し、on / Emit メソッドを追加します。

let PubSub = {
  events: {},
  on(type, handler) {
    let events = this.events
    events[type] = events[type] || []
    events[type].push(handler)
  },
  emit(type, ...datas) {
    let events = this.events

    if (!events[type]) {
      return
    }

    events[type].forEach((handler) => handler(...datas))
  }
}

ログイン後にコピー
その後、次のように使用できます:

const urls = [
  'http://url1',
  'http://url2',
  'http://url3'
]

let makeAjaxCall = (url) => {
  // do some ajax
  PubSub.emit('ajaxEnd', result)
}

let subscribe = (urls) => {
  let index = 0

  PubSub.on('ajaxEnd', (result) => {
    result = JSON.parse(result)

    if (urls[++index]) {
      makeAjaxCall(`${urls[index]}?q=${result.query}`)
    }
  })

  makeAjaxCall(urls[0])
}

ログイン後にコピー
コールバック関数と比べて革新的な変更はないようですが、この利点は、リクエスト関数と処理関数を別のモジュールに配置して結合を減らすことができることです。

2. 約束

本当の革命的な変更は Promise 仕様です。 Promise を使用すると、次のような非同期タスクを完了できます:

let makeAjaxCall = (url) => {
  return new Promise((resolve, reject) => {
    // do some ajax
    resolve(result)
  })
}

makeAjaxCall('http://url1')
  .then(JSON.parse)
  .then((result) => makeAjaxCall(`http://url2?q=${result.query}`))
  .then(JSON.parse)
  .then((result) => makeAjaxCall(`http://url3?q=${result.query}`))

ログイン後にコピー
すごいですね!同期関数のように書かれています。

心配しないでください、若者。さらに優れたものがあります:

3. ジェネレーター

ES6 のもう 1 つの大きなキラーはジェネレーター[2]です。ジェネレーター関数では、yield ステートメントを通じて関数の実行を中断し、関数の外部の next メソッドを通じてステートメントを反復できます。さらに重要なのは、next メソッドを通じて関数にデータを注入して、関数の動作を動的に変更できることです。関数。例:

function* gen() {
  let a = yield 1
  let b = yield a * 2
  return b
}

let it = gen()

it.next() // output: {value: 1, done: false}
it.next(10) // a = 10, output: {value: 20, done: false}
it.next(100) // b = 100, output: {value: 100, done: true}

ログイン後にコピー
ジェネレーターを通じて以前の makeAjaxCall 関数をカプセル化します:

let makeAjaxCall = (url) => {
  // do some ajax
  iterator.next(result)
}

function* requests() {
  let result = yield makeAjaxCall('http://url1')
  result = JSON.parse(result)
  result = yield makeAjaxCall(`http://url2?q=${result.query}`)
  result = JSON.parse(result)
  result = yield makeAjaxCall(`http://url3?q=${result.query}`)
}

let iterator = requests()
iterator.next() // get everything start

ログイン後にコピー
ああ!ロジックは非常に明確に見えますが、毎回外部からイテレータを挿入しなければならないのは非常に不快です...

心配しないで、Promise と Generator を混ぜて、どのような黒魔術が生成されるかを見てみましょう:

let makeAjaxCall = (url) => {
  return new Promise((resolve, reject) => {
    // do some ajax
    resolve(result)
  })
}

let runGen = (gen) => { 
  let it = gen()

  let continuer = (value, err) => {
    let ret

    try {
      ret = err ? it.throw(err) : it.next(value)
    } catch (e) {
      return Promise.reject(e)
    }

    if (ret.done) {
      return ret.value
    }

    return Promise
      .resolve(ret.value)
      .then(continuer)
      .catch((e) => continuer(null, e))
  }

  return continuer()
}

function* requests() {
  let result = yield makeAjaxCall('http://url1')
  result = JSON.parse(result)
  result = yield makeAjaxCall(`http://url2?q=${result.query}`)
  result = JSON.parse(result)
  result = yield makeAjaxCall(`http://url3?q=${result.query}`)
}

runGen(requests)

ログイン後にコピー
runGen 関数はオートマトンのように見えて、とても素晴らしいです!

実際、この runGen メソッドは ECMAScript 7 非同期関数の実装です:

4. 非同期関数

ES7 では、より自然な機能である async 関数[3]が導入されています。 async 関数を使用すると、次のようにタスクを完了できます:

let makeAjaxCall = (url) => {
  return new Promise((resolve, reject) => {
    // do some ajax
    resolve(result)
  })
}

;(async () => {
  let result = await makeAjaxCall('http://url1')
  result = JSON.parse(result)
  result = await makeAjaxCall(`http://url2?q=${result.query}`)
  result = JSON.parse(result)
  result = await makeAjaxCall(`http://url3?q=${result.query}`)
})()

ログイン後にコピー
上記の Promise と Generator を組み合わせたときと同様に、await キーワードも Promise を受け入れます。 async 関数では、残りのステートメントは await が完了した後でのみ実行されます。プロセス全体は、runGen 関数を使用してジェネレーターをカプセル化するのと同じです。

上記は、この記事にまとめられたいくつかの JavaScript 非同期プログラミング モードです。皆さんの学習に役立つことを願っています。

ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート