javascriptで〇秒おきに〇〇を行うなどをするにはsetInterval()という関数を使いますが、これを実行したときのエラー処理がちょっと難しいので書いておきます。
まずは普通のsetInterval()
まずは普通のsetInterval()ですが、次のように普通にやってみます。
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title></title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta name="robots" content="noindex, nofollow">
<meta name="googlebot" content="noindex, nofollow">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://unpkg.com/vue@2.5.17"></script>
</head>
<body>
<div id="app">
</div>
<script>
function helloWorld() {
console.log("こんにちは、");
try {
setInterval(function(){
console.log("日本");
},1000);
}
catch (err) {
console.log("なんかしらのエラー処理");
}
console.log("世界");
}
helloWorld();
</script>
</body>
</html>
これを実行して、consoleを見ると、次のように出力されています。(画面には何も出力されませんのでご注意を)
私はFirefoxを使っているのでFirefoxの開発者コンソールの画面ですが、次のようになっています。
最初に「こんにちは」と「世界」が出力され、次に一秒ごとに「日本」が出力されています。最近の開発者コンソールは、同じ出力をまとめてくれるので、上記のスクショだと「日本」の文字の横に「131」という文字が青地に白で書いてありますが、これが連続して出力された回数です。
わざとエラーを起こしてみる
さて、これだとエラーが起こりえないので、わざとエラーを起こしてみます。
<!-- HTML 部分は省略 -->
function helloWorld() {
console.log("こんにちは、");
try {
setInterval(function () {
throw new Error("エラーを投げるよ");
console.log("日本");
}, 1000);
} catch (err) {
console.log("なんかしらのエラー処理");
}
console.log("世界");
}
helloWorld();
最初に実行したときは、「こんにちは」「世界」が出力され、そのあとのsetInterval() 内での処理はUncaught Errorが発生して、ストップしてしまいます。
1秒間隔での処理もストップしてしまいます。
なぜ普通のtry~catch ではダメなのか
catch 節に行かないのはどうしてでしょうか。
javascript がエラーをキャッチできるのは、最初に setInterval を起動したときだけです。
「ハ?setInterval は1秒ごとに実行されてるじゃんかよ~?!」
と思うと思います。私も最初はこの説明を聞いても
「な… 何を言っているのかわからねーと思うが…おれも何をされたのか…わからなかった…」
というポルナレフ状態でした。
まぁ、setInterval を起動するのが元のtry~catch節です。setIntervalの中身の実行が1秒ごとに行われるときには、元の try~catch節 の知りえない世界でやられているので 元の try~catch節 ではキャッチできないということですね。
たとえ話でいうと、私がペットの猿のウキ吉に
「これから出かけるので、5分ごとに玄関に誰か来ないか見張りに行ってね。」
と命令したとしましょう。これが最初の実行で、setIntervalの起動です。この時点ではエラーが起こっていないということです。
しかし、私はそのあと出かけてしまうので、ウキ吉がちゃんと5分ごとに見張りをしているかは私にはわかりません…。
ウキ吉の行動がsetInterval の中のコードということになります。
setInterval()内でのエラーをキャッチ するサンプルコード
なので、setInterval() 内でのエラーをキャッチするためには、 setInterval() 内で エラー処理をする必要があります。
私はエラーがあった時の処理がわかりやすいほうがいいので、下記のようにPromiseを使う方法が好きです。
下記のサンプルでは、1秒ごとに2分の1の確率で、猿の留守番が失敗することになります。
しかし、猿の留守番は見張られているので、成功・失敗により処理がわかれます。ちゃんと留守番をしているとバナナがもらえて、留守番をしていないと叱られるということです。
<!-- HTML部分は省略 -->
function rusuban() {
console.log('ウキ吉の留守番開始')
setInterval(
function () {
console.log('時間が経過')
monkeyWatch()
.then(
function (msg) {
console.log(msg)
console.log('エライ!ご褒美のバナナだよ')
}
)
.catch(
function (err) {
console.log(err)
console.log('こらっ ダメでしょ!')
}
)
},
1000)
function monkeyWatch() {
return new Promise((resolve, reject) => {
if (Math.random() > 0.5) {
console.log('ちゃんと留守番している')
resolve('成功')
} else {
console.log('留守番していない!')
reject('失敗')
}
})
}
}
rusuban();
上記の実行結果は下記のようになります。
失敗しても、1秒後には次のターンが来ます。
ちょっとコードが長くなっちゃうのが欠点ではありますね💦