« Apache Solrを使って形態素解析とBigramを併用してテキスト検索してみる | トップページ | JavaScriptを使って複数のzipファイルをWebブラウザで並列に展開して表示してみた »

2021.01.09

Promiseよ、今まで君のことを理解できていなかったよ

最近、JavaScriptのPromiseについてよく分かっていなかったなー、と思ったことがあったのでメモ。

要は、then/catchメソッドにPromiseオブジェクトを渡して、pending状態からfulfilled状態に変わるとthenメソッドを、又はpending状態からreject状態に変わるとcatchメソッドを実行することでfunctionを非同期実行する、という点を理解すれば良い、・・・のかな?
少しサンプルコードを書いてみたので動作を見てみましょう。

sample.html


<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Promise Sample Code</title>
</head>
<body>
<h1>Promise Sample Code</h1>
<form>
<input type="button" value="sample 1" name="submit" id="btn1" />
<input type="button" value="sample 2" name="submit" id="btn2" />
<input type="button" value="sample 3" name="submit" id="btn3" />
<input type="button" value="Clear" id="clear" />
</form>
<!- ->
<hr />
<pre>
<div id="message"> </div>
</pre>
<!- ->
<script type="text/javascript" src="sample.js"></script>
<!- ->
</body>
</html>

sample.jsの必要部分を摘記しておきます。まず、コンテンツ読み込みを待つオブジェクトと画面を書き換えるメソッドを定義。


const waitLoad = () => {
return new Promise((resolve, reject) => {
document.addEventListener('DOMContentLoaded',
() => {resolve();}, false);
});
};

const clickClear = () => {
document.getElementById('clear').addEventListener('click', () =≶ {
window.location.reload(false);
}, false);
};

動作検証コード(その1)はこちら。


const mainProcess1 = () => {
document.getElementById('btn1').addEventListener('click', () => {
let promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('resolved !');
}, 3000);
});
let promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('rejected !');
}, 4000);
});

let delayResolve = () => {
console.log('delayResolve1: ');
console.log (promise1);
return promise1;
};

let delayReject = () => {
console.log('delayReject1: ');
console.log (promise2);
return promise2;
};

delayResolve()
.then((resolve, reject) => {
console.log('delayResolve2: ');
console.log(promise1);
return delayReject();
}).then((resolve, reject) => {
console.log('delayResject2: ');
console.log(promise2);
}).catch((e) => {console.log('delayReject3: ');
console.log(promise2);
console.log('reject: ');
console.log(e);});

console.log('delayResolve0:');
console.log(promise1);
console.log('delayReject0:');
console.log(promise2);
}, false);
};

// Main Routine
waitLoad()
.then(() => {mainProcess1();
clickClear();})
.catch((e) => console.log(e));

// EOF

ブラウザ・コンソールを開いてボタン"sample 1"をクリックしてコードを実行します。
ポイントは26〜42行のコード。実行結果は以下のとおり。


delayResolve1:
Promise { : "pending" }
delayResolve0:
Promise { : "pending" }
delayReject0:
Promise { : "pending" }
delayResolve2:
Promise { : "fulfilled", : "resolved !" }
delayReject1:
Promise { : "pending" }
delayReject3:
Promise { : "rejected", : "rejected !" }
reject:
rejected !

Line 26: delayResolve()内のcolsole.logを実行して"delayResolve1"、pending状態を表示
Lines 39-42: "delayResolve0", "delayReject0"を表示
Lines 27-30: 3秒(3000ms)後にdelayResolve()がfulfilled状態になって"resolved !"を返して1つ目のthen()を実行し、"delayResolve2"及びfulfilled状態を表示、さらにdelayReject()を実行
Lines 34-37: delayReject()で"delayReject1"及びpending状態を表示、1秒後にrejected状態になって"rejected !"を返し、catch節を実行、"delayReject3"、rejected状態、"reject"、変数eを表示

今度はJavaScriptにおけるエラーの取り扱いについてみてみます。


const mainProcess2 = () => {
document.getElementById('btn2').addEventListener('click', () => {
let throwError = () => {
throw new Error('Error!');
};

let delay = (ms) => {
return new Promise(resolve => setTimeout(resolve, ms));
};

delay(1000)
.then((resolve, reject) => {
console.log('done');

//throwError();

setTimeout(() => {
throwError();
},3000);
}).catch((e) => {
console.log('error:');
console.log(e);
});
}, false);
};
// Main Routine
waitLoad()
.then(() => {mainProcess2();
clickClear();})
.catch((e) => console.log(e));

このコードを実行すると、エラー発生が非同期だとcatch節が実行されないことが分かります。出力は次のとおり。


done
Uncaught Error: Error!
throwError http://localhost/~tonop/examples/202101/sample/sample.js:126
mainProcess3 http://localhost/~tonop/examples/202101/sample/sample.js:140
setTimeout handler*mainProcess3/</< http://localhost/~tonop/examples/202101/sample/sample.js:139
promise callback*mainProcess3/< http://localhost/~tonop/examples/202101/sample/sample.js:134
mainProcess3 http://localhost/~tonop/examples/202101/sample/sample.js:124
http://localhost/~tonop/examples/202101/sample/sample.js:186
promise callback* http://localhost/~tonop/examples/202101/sample/sample.js:184

そこで、Lines 17-19をコメントアウトしてLine 15をイキにしたときの出力は以下のとおり。


done
error:
Error: Error!
throwError http://localhost/~tonop/examples/202101/sample/sample.js:126
mainProcess3 http://localhost/~tonop/examples/202101/sample/sample.js:137
promise callback*mainProcess3/< http://localhost/~tonop/examples/202101/sample/sample.js:134
mainProcess3 http://localhost/~tonop/examples/202101/sample/sample.js:124
http://localhost/~tonop/examples/202101/sample/sample.js:186
promise callback* http://localhost/~tonop/examples/202101/sample/sample.js:184

今度はcatch節を実行しています。

最後におまけ。then節の中にresolveとrejectを両方書くこともできます。


const mainProcess3 = () => {
document.getElementById('btn3').addEventListener('click', () => {
let delay = (ms) => {
return new Promise(resolve => setTimeout(resolve, ms));
};

delay(3000)
.then(
resolve => {
console.log('resolved1 !');
reject();
},
reject => {
console.log('rejected1 !');
reject();
}
).then(
resolve => {
console.log('resolved2 !');
},
reject => {
console.log('rejected2 !');
}
);
}, false);
};

waitLoad()
.then(() => {mainProcess1();
mainProcess2();
mainProcess3();
clickClear();})
.catch((e) => console.log(e));

出力は以下のとおり。


resolved1 !
rejected2 !

この記事を書くに当たり以下のサイトをはじめ色々なサイトを参考にしました。
【JavaScript】ちゃんと理解しておきたいPromiseの勘所など:KDEブログ
Promiseとthenのメソッドチェーン(直列・並列・値の受け取り・引数):Qiita
ユーザのブラウザで起きた JavaScript のエラーを収集する:Qiita
JavaScriptのエラーついて知っておくべきこと:Qiita
ありがとうございます。

|

« Apache Solrを使って形態素解析とBigramを併用してテキスト検索してみる | トップページ | JavaScriptを使って複数のzipファイルをWebブラウザで並列に展開して表示してみた »

パソコン・インターネット」カテゴリの記事

コメント

コメントを書く



(ウェブ上には掲載しません)




« Apache Solrを使って形態素解析とBigramを併用してテキスト検索してみる | トップページ | JavaScriptを使って複数のzipファイルをWebブラウザで並列に展開して表示してみた »