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(); |