5
4

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 3 years have passed since last update.

Promiseの処理をキャンセルする

Last updated at Posted at 2020-02-09

Promise で Web Speech API をラップして使っていましたが、キャンセルできるように実装するのに試行錯誤しました。想定していたような動きが実現できたので、メモを残しておきます。

See the Pen Web Speech API with Promise by 七誌 (@7shi) on CodePen.

↑ エラーになる場合は一度 CodePen を開いてから、この記事をリロードしてください。

概要

前回の記事では正常終了 onendresolve、異常終了 onerrorreject として扱いました。

function speak(lang, text) {
    return new Promise((resolve, reject) => {
        let u = new SpeechSynthesisUtterance(text);
        u.lang = lang;
        u.onend = resolve;
        u.onerror = reject;
        speechSynthesis.speak(u);
    });
}

読み上げ中に speechSynthesis.cancel() を呼ぶことでキャンセルできます。通常終了と同じ onend イベントが発生するため、イベントではキャンセルされたことが検知できません。

何らかの手段でキャンセルされたことを通知する必要があります。

戻り値

resolve への引数は await を通して戻り値になります。

ブラウザのコンソール
> p = new Promise((resolve, reject) => resolve(123))
> await p
123

これを利用して正常終了かキャンセルかを戻り値で区別するように speak を実装します。例外を無視するため終了と同じ扱いとします。

let stop = () => false;
function speak(lang, text) {
  return new Promise((resolve, reject) => {
    let speakend = cancel => {
      speakend = () => false;
      if (cancel) speechSynthesis.cancel();
      resolve(cancel);
      return cancel;
    };
    stop = () => speakend(true);
    let u = new SpeechSynthesisUtterance(text);
    u.lang = lang;
    u.onend = u.onerror = () => speakend(false);
    speechSynthesis.speak(u);
  });
}

キャンセルするには外部から stop() を呼びます。Promise のコンストラクタで stop を書き換えて speakend 経由で resolve(true) を呼べるようにしておくことで、終了イベント onend よりも先に終了させます。

利用方法

複雑さは Promise の中に閉じ込めたため、利用側のコードは簡単になります。

button.onclick = async function() {
  if (stop()) return;
  button.textContent = "Stop";
  for (let [element, lang, text] of texts) {
    element.classList.add("speaking");
    let cancel = await speak(lang, text);
    element.classList.remove("speaking");
    if (cancel) break;
  }
  button.textContent = "Start";
};

ループによっていくつかのテキストを読み上げます。speaking をマークすることで読み上げ個所を示します。await speak() から戻ってマークを解除して、キャンセルされていればループから抜けます。

reject で例外によってキャンセルを通知することも可能ですが、今回は resolve によって戻り値で通知した方が利用側のコードが簡単になると判断しました。

戻り値は正常終了のときに true にした方が自然かもしれませんが、今回はキャンセルに注目して値を設定しました。

参考

Web Speech API の使い方は以下の記事を参照してください。

5
4
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
5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?