## そもそも同期処理とは
プログラムに記述された順序通りに実行される実行方式。
同期処理サンプルプログラム
const calculate = () => {
return 1 + 1
}
const message = "1+1="
const result = calculate()
console.log(message + result)
実行結果
$ node synchronous.js
1+1=2
上の例は問題ないが、もしcalculate
関数がとても時間のかかる処理であれば、ユーザーにとって大きな待ち時間が生まれる。
それを解決するために非同期処理が存在する。
非同期処理とは
タスクの実行完了を待たず、並行して次の処理を行う実行方式。
インターネットを通じて外部のデータを操作するとき(Web Apiを叩く)や、データベースにクエリを発行する際に利用される。
身近にある非同期処理が使われたサービスは、Google Mapである。Google Mapは地図の読み込み時にもユーザーは操作できる。
ここでは、非同期処理を使ってgithubの提供するAPIを叩いてユーザー名を取得させるプログラムを書いてみる。
非同期処理サンプルプログラム
import fetch from 'node-fetch'
const getUserName = () => {
const url = "https://api.github.com/users/XXXXXXXXX" //XXXXXXXにはgithubのユーザー名を入れてください
fetch(url).then(res => res.json()) //非同期処理であるfetchメソッドで定義したurlからデータを取得して、取得したデータを引数(res)としてjsonメソッドでjson化する処理をthenメソッドでチェーン
.then(json => { //成功時にres.json()の戻り値を引数(json)として処理を記述
console.log("これは非同期処理成功時のメッセージです")
console.log(json.login) //ユーザー名を表示
return json.login //ユーザー名を戻り値としてgetUserNameにreturn
}).catch(error => { //失敗時の処理
console.log("これは非同期処理失敗時のメッセージです", error)
return null
})
};
const message = "githubユーザー名:"
const userName = getUserName()
console.log(message + userName)
実行結果
githubユーザー名:undefined
これは非同期処理成功時のメッセージです
XXXXXXXXXX
※XXXXXXXXにはgithubユーザー名が入ります。
githubユーザー名がundefined
と表示されてしまった。
これは、fetch
が非同期で処理するメソッドであるために、getUserName
関数の処理が完了してUserName
に結果が代入される前に`console.log(message+userName)実行されたからである。
これを解決するために、この非同期処理を、同期処理のように実行順序を制御する必要がある。
2通り方法がある。
Promiseを使う方法、async/awaitを使う方法である。
どちらの方法も非同期の処理が完了するまで次の処理に進まないようにしてくれるものである。
Promiseを使う方法
Promiseサンプルプログラム
import fetch from 'node-fetch'
const getUserName = () => {
return new Promise((resolve, reject) => { //new PromiseでPromiseのインスタンスを作成、その引数にresolve,rejectを引数にした非同期処理の関数を記述
const url = "https://api.github.com/users/XXXXXXXX" //XXXXXXXにはgithubのユーザー名を入れてください
fetch(url).then(res => res.json()) //非同期処理であるfetchメソッドで定義したurlからデータを取得して、取得したデータを引数(res)としてjsonメソッドでjson化する処理をthenメソッドでチェーン
.then(json => { //成功時にres.json()の戻り値を引数(json)として処理を記述
console.log("これは非同期処理成功時のメッセージです")
console.log("非同期処理が成功")
return resolve(json.login) //ユーザ名をreturn
}).catch(error => { //失敗時
console.log("非同期処理失敗", error)
return reject(null)
})
})
};
const message = "githubユーザー名:"
getUserName().then(userName => { //成功すればthenメソッドが実行される。userNameにはgetUserName関数のjson.loginが入っている。
console.log(message + userName)
}).catch(()=>{ //失敗すればcatchメソッドが実行される
console.log("rejectしました")
})
実行結果
$ node promise.js
非同期処理が成功
githubユーザー名:XXXXXXXXX
Promise
を使うことで非同期処理内(この例ではfetch
メソッド以下)でresolve
またはreject
が出てくるまで次の処理に進まないようにしてくれる。
Promiseには3つの状態がある
Promise
には、PromiseStatus
という3つのステータスがある。
-
pending
: 初期状態(未処理) -
resolved
: 成功状態 -
rejected
: 失敗状態
new Promise
で作られたPromise
オブジェクトのPromiseStatus
はpending
(初期状態)となり、成功した場合はresolved
(成功状態)となり、失敗した場合、rejected
(失敗状態)となる。resolved
の場合はthen
メソッドが実行され、rejected
の場合はcatch
メソッドが実行される。
async/await
async/awaitサンプルプログラム
import fetch from 'node-fetch'
const getAddress = async() => { //非同期処理を伴う関数定義時の引数をasync()にする
const message = "githubユーザー名:"
const url = "https://api.github.com/users/XXXXXXXX" //XXXXXXXにはgitのユーザー名を入れてください
const json = await fetch(url) //非同期処理を伴う関数実行時にawaitをつける
.then(res => { //成功時にfetchメソッドで取得したデータを引数として処理を記述
console.log("非同期処理成功")
return res.json() //resをjson化
}).catch(error => { //失敗時の処理
console.log("非同期処理失敗", error)
return null
});
const userName = json.login //ユーザー名を代入
console.log(message + userName)
}
getAddress()
実行結果
$ node AsyncAwait.js
非同期処理成功
githubユーザー名:XXXXXXXX
非同期処理を伴う関数定義の引数をasync()
にして、非同期処理を伴う関数実行時にawait
をつけることによって非同期処理が完了するまで次の処理に進まないようにしてくれる。
参考
【ES6】 JavaScript初心者でもわかるPromise講座
非同期処理の完了を待つ方法!Promise&async/await【分かりすぎて怖いJavaScript入門】