JSの非同期処理の歴史、コールバック関数(ES5以前)→Promise(ES6)→async/await(ES8)の違いについて
前回の続き
参考URL
https://sbfl.net/blog/2016/07/13/simplifying-async-code-with-promise-and-async-await/
注意:
XMLHttpRequest()は、サーバサイドのnode.js(コマンドライン)では使えないので、ブラウザのF12押してディベロッパーツールのコンソールにコピペして実行する。
最後のサンプルみたいにrequest関数で実装するのが正解だな。
1, コールバック関数を使わないと、非同期関数をうまく使えない
1 2 3 4 5 6 |
// サーバのa.txtを非同期関数で読み込む const xhr = new XMLHttpRequest(); xhr.open('GET', 'http://localhost/a.txt'); xhr.send(); // 非同期関数が終わってない状態なのでエラー console.log(xhr.responseText); |
2, 非同期関数に、コールバック関数を渡して完了してから処理を行えばOK!
1 2 3 4 5 |
const xhr = new XMLHttpRequest(); xhr.open('GET', 'http://localhost/a.txt'); // addEventListenerは非同期関数 xhr.addEventListener('load', (event) => console.log(xhr.responseText)); xhr.send(); |
3, 非同期関数+コールバック関数が沢山だとネストがヒドい!(コールバック地獄)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// サーバのa.txt -> b.txt -> c.txtの順で読み込んでいく const xhr = new XMLHttpRequest(); xhr.open('GET', 'http://localhost/a.txt'); xhr.addEventListener('load', (event) => { console.log(xhr.responseText) const xhr2 = new XMLHttpRequest(); xhr2.open('GET', 'http://localhost/b.txt'); xhr2.addEventListener('load', (event) => { console.log(xhr2.responseText) const xhr3 = new XMLHttpRequest(); xhr3.open('GET', 'http://localhost/c.txt'); xhr3.addEventListener('load', (event) => { console.log(xhr3.responseText) }); xhr3.send(); }); xhr2.send(); }); xhr.send(); |
3, Promise(プロミス)とは何か?基本文法は、こんな感じ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
// 最初にPromiseオブジェクトを生成 const promise = new Promise( // 正常orエラー終了時の関数名を決める (ok, ng) => { // 適当な確率でエラーを起こす if(Math.random() > 0.5 ){ //...非同期な処理を行う。 ok('正常終了'); // 正常終了関数 }else{ // エラーで異常終了関数 ng('エラーです!'); } } ); // 非同期処理が完了したら、thenメソッドがコールされる promise.then((result) => console.log(result) // エラー処理 ).catch((e) => console.log(e) ); |
4, 非同期処理のコールバック地獄を解消するPromise(プロミス)で記述する。
ネストが深くならず、関数化されてて良い感じ!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
// ファイルのオープン関数 function openFile(url){ // Promiseオブジェクト生成 const promise_obj = new Promise((ok, ng) =>{ // HTML通信オブジェクト生成 const xhr = new XMLHttpRequest(); // メソッドはGET xhr.open('GET', url); // loadが完了したら、内容をコンソールに表示して正常終了 xhr.addEventListener('load', (e) => { console.log(xhr.responseText) ok(xhr) }); // サーバへ送信 xhr.send(); }); // 次のthenメソッド用にreturn return promise_obj; } // サーバのa.txt -> b.txt -> c.txtの順で読み込んでいく openFile('http://localhost/a.txt') .then((xhr) => openFile('http://localhost/b.txt')) .then((xhr) => openFile('http://localhost/c.txt')) /* こういう配列的な書き方でもOK const promise = Promise.all([openFile('http://localhost/a.txt'), openFile('http://localhost/b.txt'), openFile('http://localhost/c.txt')]); */ |
5, ES8(ECMAScript2017)では、Promiseの糖衣構文(syntax sugar=シンプルで分かりやすい書き方)のasync/awaitが登場した!
a, Promiseオブジェクトを返す非同期関数の前には、awaitをつける
b, awaitを使うには、async function内でないと駄目!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
// URLリクエストするパッケージ const request = require("request"); // Promiseを返す非同期関数 function openFile(filename){ // urlとメソッドが決定 const param = { url: "http://localhost/" + filename, method: "GET" } // 返り値はPromiseオブジェクト return new Promise((ok, ng)=>{ // 非同期関数のrequestで指定されたURLへアクセス request(param, function (error, response, body) { if(error){ ng("ページを取得できませんでした"); }else{ console.log(body); ok("取得できました"); } }) }) } async function loadAllFiles() { const xhr1 = await openFile('a.txt'); const xhr2 = await openFile('b.txt'); const xhr3 = await openFile('c.txt'); } loadAllFiles(); |