Chapter 04

実例で学ぶPromise (2)

いーちゃん
いーちゃん
2021.08.15に更新

Promiseの扱いに慣れてくると、onloadなどもPromiseで書きたくなります(なりませんか?)。
<input type="file">でアップロードした画像を<canvas>に描画する処理を例にして、その書き方を紹介します。

function onChange(event) {
  const file = event.target.files[0];
  const reader = new FileReader();
  reader.readAsDataURL(file);
  reader.onload = () => {
    const img = document.createElement("img");
    img.src = reader.result;
    img.onload = () => {
      const canvas = document.getElementById("canvas");
      const ctx = canvas.getContext("2d");
      ctx.drawImage(img, 0, 0);
    };
  }
}
document.getElementById("input").addEventListener(onChange);

のように、ロード処理を待ってから何か処理を実行したいときです。

自分でPromiseを用いてコールバック関数をラップします。

async function onChange(event) {
  const file = event.target.files[0];
  const reader = new FileReader();
  reader.readAsDataURL(file);
  await new Promise(resolve => reader.onload = () => resolve());

  const img = document.createElement("img");
  img.src = reader.result;
  await new Promise(resolve => img.onload = () => resolve());

  const canvas = document.getElementById("canvas");
  const ctx = canvas.getContext("2d");
  ctx.drawImage(img, 0, 0);
}
document.getElementById("input").addEventListener(onChange);

ネストがなくなり、可読性が増しました。
具体的には、

await new Promise(resolve => reader.onload = () => resolve());

この部分が変化しています。reader.onloadのコールバック関数でresolveしてあげれば、ロード完了時にresolveされる、それをawaitするのでロードが完了するまで待機する、という処理が完成します。
なお、await演算子を利用しているため関数にはasyncをつけておきましょう。

直列処理においてコールバックのネストが深くなることを防ぐための手段としても、Promiseは有効なのです。