30
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

JavaScriptAdvent Calendar 2017

Day 7

async/await で Modal の Queueing

Last updated at Posted at 2017-12-08

モーダル被ってませんか?

モーダルを実装するにあたり「表示条件によって被って表示されてしまう」「例外発生時の仕組みがない」という問題に直面したことはありませんか?また、ユーザーステータスによって数種類のモーダルを異なる順番で表示したい、という要件もあるかと思います。

本項では ES2017 の async/await を使ってQueueing を実装する方法を紹介します。vanilla なので、どのフレームワークやアーキテクチャを採用していても導入出来ます。

Untitled.gif

sample: https://github.com/takefumi-yoshii/async-await-modal-queue

サンプルは chrome で、babel や webpack を使用せずにそのまま実行出来ます。もちろんプロダクトコードは適宜バンドラや polyfill を利用してください。

モーダル画面を一つのPromiseと捉える

Promise といえば、XHR で json を取得したり、アニメーションの完了を待ったりする非同期処理のためのもの、というイメージが強いかと思います。「モーダルが表示されてから閉じるまで」などのUIの一連の操作も、期待値を待っている非同期処理と同等と捉えることが出来ます。

モーダル画面を表示する際の関数に、Promiseを発行させます。そして「閉じるボタン押下、背景押下」などの閉じる相当の行為で、発行された Promise を resolve する様に仕込んでおきます。

promises.js
export function clickModalCloseButton () {
  return (() => new Promise(resolve => {
    document.getElementById('modal-close-button')
      .addEventListener('click', resolve, { once: true })
    })
  )
}

async/await で Queueing

async function に無限ループを置きます。Queue配列に格納された Promise をひとつずつ取り出して await します。Queueが空になったら、無限ループを break。async function 自体の戻り値も Promise なので、クロージャの末尾まで到達・例外throw で resolve・reject となります。

asyncs.js
let queue = []

async function playQueue() {
  while (true) {
    const { resolvedModalPromise, value } = queue[0]
    await openModalAnimation(value)
    await resolvedModalPromise()
    await closeModalAnimation()
    queue.shift()
    if (queue.length === 0) break
  }
}

Queueの再生

今度は別の async function で、
Queueに溜まったモーダルを再生させるイベントを待ちます。ボタン押下イベントを待つものもまた Promise として扱い、ループの中で await します。

promises.js
export function clickPlayButton () {
  return new Promise(resolve => {
    document.getElementById('play-button')
      .addEventListener('click', resolve, { once: true })
  })
}

「Queueの再生が全て終わったら、また再生ボタン押下を待つ」というループですね。

asyncs.js
export async function watchPlay() {
  while (true) {
    await clickPlayButton() // Promise
    await playQueue() // async function
  }
}

Redux や Flux では、特定の Action を待つ Promise を発行すれば良いですね。redux-saga や vuex-saga を導入していれば、async/await と同じ様に saga はデザインされているので、そちらに組み込みましょう。サンプルは分かり易さをとったので、jQueryライクな書き方を随所にしていますが、ご承知下さい。

30
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
30
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?