⭐️🎀 JavaScript Visualized: Promises & Async/Await

JavaScriptのコードを扱っていて、思った通りに動かなかった経験はありますか?関数がランダムな、予測不能なタイミングで実行されたり、実行が遅延したりするように感じたことはありませんか?もしかしたら、ES6によって導入されたクールな新機能、Promisesと関わっている場合があります!

何年か前の私の好奇心が報われて、またしても睡眠不足の夜を利用してアニメーションを作成する時間を得ることができました。今回はなぜPromisesを使うのか、バックエンドでどのように動作するのか、そして最もモダンな方法でPromisesを書くにはどうするかについて話しましょう。

もしまだ私の「JavaScriptのイベントループ」についての前の投稿を読んでなければ、先にそれを読むのが役立つかもしれません!コールスタック、Web API、それにキューについての基本的な知識を前提にしつつ、再びイベントループについてカバーしますが、今回はちょっとワクワクする追加機能についても触れます🤩


既にPromisesに少し慣れている人のために、スクロール時間を節約するためのショートカットをいくつか紹介します。


はじめに

JavaScriptを書くとき、他のタスクに依存するタスクを扱うことがよくあります!例えば、画像を取得して、それを圧縮し、フィルターを適用して保存したいとしましょう 📸

最初にしなければならないことは、編集したい画像を_取得_することです。getImage関数がこれを処理できます!画像が正常にロードされたら、その値をresizeImage関数に渡すことができます。画像が正常にリサイズされたら、applyFilter関数で画像にフィルターを適用します。画像が圧縮されフィルターが追加されたら、画像を保存し、ユーザーにすべてが正しく動作したことを知らせたくなります!🥳

最終的に、こんな感じになります。

画像

うーん...何か気づきますか?それは..._まあまあ_なのですが、素晴らしいわけではありません。多くのネストされたコールバック関数が先のコールバック関数に依存しているため、コードを読むのがかなり難しくなってしまいます!

幸いなことに、今ではPromisesというものがあり、こうしたシチュエーションで助けてくれます!それではPromisesが何なのか、そしてこのような状況でどのように役立つのかを見てみましょう!😃


Promiseの構文

ES6でPromisesが導入されました。多くのチュートリアルではこんなことが書かれています:

「Promiseは、将来的に解決されるか、拒否されるかもしれない値のプレースホルダーです」

はい...その説明は、私に事が明確になるどころか、Promiseが奇妙で曖昧で予測不可能な魔法の一部に思えてきました。それでは、Promisesが_実際に_何なのかを見てみましょう。

Promiseコンストラクタを使用して、コールバックを受け取ることでPromiseを作成できます。オーケー、試してみましょう!

Alt Text

待ってほら、何が返ってきたの?

Promiseは、状態([[PromiseStatus]])と([[PromiseValue]])を含むオブジェクトです。上記の例では、[[PromiseStatus]]の値が"pending"であり、Promiseの値はundefinedです。

心配しないでください - このオブジェクトとやり取りする必要はありませんし、[[PromiseStatus]][[PromiseValue]]のプロパティにアクセスすることさえできません!しかし、これらのプロパティの値は、Promisesを使うときに重要です。


PromiseStatusの値、つまり状態は以下の3つのうちの1つです:

  • fulfilled: Promiseがresolvedされています。すべてが順調で、Promise内でエラーは発生していません🥳
  • rejected: Promiseがrejectedされています。ああ、何かが間違っています..
  • pending: Promiseはまだ解決も拒否もされていません(まだ)、Promiseはpending状態です。

このすべては素晴らしく聞こえますが、_いつ_Promiseの状態が"pending""fulfilled"、または"rejected"になるのでしょうか?そしてその状態はなぜ重要なのでしょうか?

上記の例では、Promiseコンストラクタに単純なコールバック関数() => {}を渡しました。しかし、このコールバック関数は実際には2つの引数を受け取ります。最初の引数の値、よくresolveresと呼ばれるものは、Promiseを解決するときに呼び出されるべきメソッドです。2番目の引数の値、よくrejectrejと呼ばれるものは、何かが間違っていてPromiseが拒否されるべきときに呼び出されるメソッドです。

画像

resolverejectメソッドを呼び出すとどうなるのかを見てみましょう!私の例ではresolveメソッドをresrejectメソッドをrejと呼びました。

画像

素晴らしい!ついに"pending"状態とundefined値を無くす方法を知りました!resolveメソッドを呼び出した場合のPromiseの状態"fulfilled"であり、rejectedメソッドを呼び出した場合のPromiseの状態は"rejected"です。

Promiseの[[PromiseValue]]の値、はresolvedまたはrejectedメソッドの引数として渡される値です。

興味深い事実ですが、この記事を校正してもらったジェイク・アーカイバルドが、現在Chromeには状態が"resolved"ではなく"fulfilled"と表示されるというバグがあると指摘してくれました。マティアス・ビネンスのおかげで、現在Canaryでは修正されています!🥳🕺🏼

不明なツイートメディアのコンテンツ

ChromeとSafariではこれを"resolved"とするPromiseを呼びますが、これは真実ですが、少し誤解を招くかもしれません…

09:21 AM - 09 Apr 2020

ツイッター返信アクションツイッターリツイートアクションツイッターいいねアクション


さて、もう少し具体的に制御可能なあやふやなPromiseオブジェクトについて知ることができました。しかし、これは何に使われるのでしょうか?

はじめに紹介したセクションでは、画像を取得し、圧縮し、フィルターを適用し、保存する例を見ました!最終的に、これはネストされたコールバックの混乱になりました。

幸いにも、Promisesはこれを解決するのに役立ちます!まず、コードブロック全体を書き直して、それぞれの関数がPromiseを返すようにしましょう。

画像がロードされてすべてがうまくいった場合は、ロードされた画像でPromiseを解決しましょう!それ以外の場合、ファイルをロード中にどこかでエラーが発生したら、発生したエラーでPromiseを拒否しましょう。

画像

ターミナルでこれを実行して何が起こるか見てみましょう!

画像

クール!期待通りに解析されたデータの値を持つPromiseが返されました。

しかし...これでどうすれば良いのでしょう?私たちはそのすべてのPromiseオブジェクトに興味がありません、私たちはデータの値だけに興味があります!幸い、Promiseの値を取得するための組み込みメソッドがあります。以下の3つのメソッドをPromiseにアタッチできます:

  • .then(): Promiseが_解決_された後に呼び出されます。
  • .catch(): Promiseが_拒否_された後に呼び出されます。
  • .finally(): _常に_呼び出されます。Promiseが解決されたか拒否されたかに関わらず。

画像

.thenメソッドは、resolveメソッドに渡された値を受け取ります。

画像

.catchメソッドは、rejectedメソッドに渡された値を受け取ります。

画像

ついに、その全てのPromiseオブジェクトなしで解決された値を得ることができました!これで、この値を自由に使えます。


FYI、Promiseが常に解決されるか、常に拒否されることがわかっている場合は、Promise.resolveまたはPromise.rejectを、

こちらの記事はdev.toの良い記事を日本人向けに翻訳しています。
https://dev.to/lydiahallie/javascript-visualized-promises-async-await-5gke