みんさん、こんにちは。どんぶラッコです。
本日は同期処理・非同期処理について例え話を使いながら解説をしてみようと思います。
JavaScriptなどで出てくるこの言葉、混乱する人も多いのではないでしょうか?
今回はそんな同期処理・非同期処理を例え話を使って理解しちゃいましょう!
目次
お風呂に入る・洗濯機を回す・洗濯物を干す…
ここでは例として、
- お風呂に入る
- 洗濯機を回す
- 洗濯物を干す
という処理を考えてみましょう。
すると、同期処理・非同期処理は下記のように表現することができます。
同期処理とは
同期処理はつまり、上から順番に処理を実行していきます。今回の例で説明すると、洗濯機を回す処理を開始したら、洗濯機が停止するまで何もしてはいけません。書かれている順番に処理が実行される、というプログラムの原則に則った書き方ですね。
さて、ここで注目して欲しいのは、各処理の主語です。書き出してましょう。
お風呂に入るのは? -> 私
洗濯をするのは? -> 洗濯機
洗濯物を干すのは? -> 私
つまり、洗濯機が頑張って洗濯物を洗ってくれている間、私というリソースが使用されずに放置されてしまうことになってしまいます。
非同期処理とは
ここで登場するのが非同期処理です。つまり、洗濯機が頑張って洗濯してくれている間にお風呂に入ってしまうのです。
洗濯が終わり、洗濯機が「ピーッピーッ」と終わった合図を出してくれてたら洗濯物を干す…という戦略です。
すると、リソースを無駄にすることなく行動、つまり処理を進めることができますよね。
JavaScriptに落とし込むと
- 私
- JavaScript自体の処理
- 洗濯機
- 外部リソースとの連携作業
- サーバーサイド処理、ファイル読み込み、Ajax処理… etc.
- 外部リソースとの連携作業
といった感じ。
非同期通信は処理の順番に気をつけろ!
非同期を行う際に重要になってくるのは、処理の順番を決めることです。
もし、洗濯が”終わったら”、というように処理を書かなかった場合、「洗濯物を干す」という処理が洗濯が終わる前に実行されてしまい、エラーになってしまいます。
なので、「洗濯物を干す」という処理は、「洗濯機を回す」という処理が終わった後に実行されてほしい処理である、ということができます。
JavaScriptで実装してみよう
今回の処理
実際のコード
処理が実行されない例
/* ------------------
console.logに何も表示されないパターン
------------------ */
const fs = require('fs')
let result
fs.readFile('./src/1.txt', 'utf-8', (err, data) => {
result = data
})
console.log(result)
fs.readFile('./src/2.txt', 'utf-8', (err, data) => {
result = data
})
console.log(result)
fs.readFile('./src/3.txt', 'utf-8', (err, data) => {
result = data
})
console.log(result)
実行結果
undefined
undefined
undefined
処理結果の順番がバラバラになる例
const fs = require('fs')
fs.readFile('./src/1.txt', 'utf-8', (err, data) => {
console.log(data)
})
fs.readFile('./src/2.txt', 'utf-8', (err, data) => {
console.log(data)
})
fs.readFile('./src/3.txt', 'utf-8', (err, data) => {
console.log(data)
})
実行結果
this is 1
this is 3
this is 2
コールバック関数をうまく使う例
ここから、想定通りに動くコードの例です。
const fs = require('fs')
fs.readFile('./src/1.txt', 'utf-8', (err, data) => {
console.log(data)
fs.readFile('./src/2.txt', 'utf-8', (err, data) => {
console.log(data)
fs.readFile('./src/3.txt', 'utf-8', (err, data) => {
console.log(data)
})
})
})
実行結果
this is 1
this is 2
this is 3
Promiseを活用する例
const fs = require('fs')
function rFilePromise (name) {
return new Promise((resolve) => {
fs.readFile(name, 'utf-8', (err, data) => {
resolve(data)
})
})
}
rFilePromise('./src/1.txt')
.then((text) => {
console.log(text)
return rFilePromise('./src/2.txt')
})
.then((text) => {
console.log(text)
return rFilePromise('./src/3.txt')
})
.then((text) => {
console.log(text)
})
Generateを使う例
const fs = require('fs')
function rFileGenerate (g, name) {
fs.readFile(name, 'utf-8', (err, data) => {
g.next(data)
})
}
const g = (function * () {
const one = yield rFileGenerate(g, './src/1.txt')
console.log(one)
const two = yield rFileGenerate(g, './src/2.txt')
console.log(two)
const three = yield rFileGenerate(g, './src/3.txt')
console.log(three)
})()
g.next()
async, await の活用
const fs = require('fs')
function rFilePromise (name) {
return new Promise((resolve) => {
fs.readFile(name, 'utf-8', (err, data) => {
resolve(data)
})
})
}
async function readText () {
const one = await rFilePromise('./src/1.txt')
console.log(one)
const two = await rFilePromise('./src/2.txt')
console.log(two)
const three = await rFilePromise('./src/3.txt')
console.log(three)
}
readText()
ちなみに、より体系的にインプットしたい方はこちらの書籍がおすすめです!