JavaScriptのPromiseがあまりにも分からなかったので、手を動かしながらいろいろと動きを勉強したので、メモしておきます✍
Promiseの基本
Promiseとは、MDNを見てみると下記のような説明がされています。
非同期処理の実行の完了や失敗を検知することが出来るようなObjectみたいですね👀
Promise オブジェクトは非同期処理の最終的な完了処理(もしくは失敗)およびその結果の値を表現します。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise
MDNのサンプルコードが下記です。new Promise()
でPromiseのオブジェクトを作成して、functionの引数のresolve
にthen
で渡したcallback、rejectにcatch
で渡したcallbackが入ります。※thenが成功、catchが失敗した際に実行されます。
下記のサンプルでは、0.3秒後にcallbackに引数'foo'
を渡して実行するPromiseのオブジェクトを作成して、
promise1.then
に引数の値を標準出力するfunctionをcallbackとして設定しているので、foo
がコンソールに表示されます。
promise1
自体を標準出力するとPromiseのオブジェクトが返却されます。
var promise1 = new Promise(function(resolve, reject) { setTimeout(function() { resolve('foo'); }, 300); }); promise1.then(function(value) { console.log(value); // expected output: "foo" }); console.log(promise1); // expected output: [object Promise]
いろいろ動かして試してみる。
まずは、検証用に下記のようなものを用意しました。
sleep
は、timeで指定した時間後にcallbackを実行する遅延処理です。その他については普通に標準出力する処理です。
function sleep(time, callback) { setTimeout(() => { callback(null) }, time); } const exec_1 = () => { console.log('async 1 execute!!') } const exec_2 = () => { console.log('async 2 execute!!') } const exec_3 = () => { console.log('async 3 execute!!') }
下記のような処理を実行するとresultに書いたような結果になります。sleepで1秒まっているので、
exec_2
よりもexec_3
が先に実行されてしまっていますね。
exec_1() sleep(1000, exec_2) exec_3() /* result async 1 execute!! async 3 execute!! async 2 execute!! */
これをPromise
を使って、exec_2、exec_3の順番で実行されるようにしていこうと思います。
exec_2のあとにexec_3を実行するために遅延処理をPromiseにする。
まずは、sleep
をPromiseのオブジェクトを返却するようにして、完了のタイミングで処理を実行出来るようにします。
function delay(time) { return new Promise((resolve) => { sleep(time, resolve) }) }
👇delay
とthen
を使って処理修正したのが下記です。
delay(1000).then(exec_2)
で1秒待ったあとにexec_2
が実行されます。
then
の返り値もまたPromise
のオブジェクトになるので、then(exec_2).then(exec_3)
によってexec_2
実行後にexec_3
が実行されます。
そのため結果は、result
のようになります。
exec_1() delay(1000).then(exec_2).then(exec_3) /* result async 1 execute!! // 1 sec async 2 execute!! async 3 execute!! */
thenのなかで更にPromiseを作成して各メソッドを1秒間隔で実行する
こうなってくると1秒間隔で各メソッドを実行したくなるわけですが、単純に下記のようにすると上手くいかないわけですよ。。
delay(1000).then().then(delay(1000).then(exec_3)) /* result async 1 execute!! // 2 sec async 2 execute!! async 3 execute!! */
なぜ上手く行かないかというと下記のような処理になってしまっていたわけですね。。。
- exec1が実行 ->
async 1 execute!!
が表示 - 1秒待つ
- exec2が実行予約(1秒後) ->
delay(1000).then(exec_2)
が実行 - exec3が実行予約(1秒後) ->
delay(1000).then(exec_3)
が実行 - 1秒待つ(exec_2、exec_3のdelayはほぼ同時に実行されるため)
- exec2が実行 ->
async 2 execute!!
が表示 - exec3が実行 ->
async 2 execute!!
が表示
上記を考慮して修正したコードが下記です。
exec_1() delay(1000).then( () => { return new Promise( resolve => { exec_2(); delay(1000).then(resolve) } ) } ).then(exec_3) /* result async 1 execute!! // 1 sec async 2 execute!! // 1 sec async 3 execute!! */
- exec1が実行 ->
async 1 execute!!
が表示 - 1sec待つ ->
delay(1000)
が実行 - exec_2と1秒後にcallbackを実行するPromiseが設定される ->
delay(1000).then(..)
が実行される - thenが実行されて
exec_2(); delay(1000).then(exec_3)
が実行されるため、exec_2が実行されたあと1秒待ってexec_3が実行される。 ->exec_2(); delay(1000).then(resolve)
が実行される
これでresult
のように1秒間隔で実行される処理が実現出来ました🙌
おわりに
今回はJavaScriptのPromiseが分からなすぎたので、いろいろ実行しながら学んでみました。
やはりドキュメントを読むだけでなくて、手を動かすといろいろと理解が深まりますね。
それでは👋