Help us understand the problem. What is going on with this article?

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

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

See the Pen Web Speech API with Promise by 七誌 (@7shi) on 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 の使い方は以下の記事を参照してください。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした