1. miyarappo

    Posted

    miyarappo
Changes in title
+【JavaScript】非同期処理/Promise/asyncと聞くとにビビっちゃう人を救う
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,240 @@
+## 非同期とは何か?
+JavaScriptは**非同期**で実行されます。
+非同期で動くとはどういうことかコードを実行して体験してみます。
+
+```sample.js
+console.log('#1')
+
+setTimeout(() => {
+ console.log('#2')
+}, 500)
+
+console.log('#3')
+```
+
+ブラウザのコンソールかNode.jsで実行してみましょう。
+Node.jsで実行するときはターミナルでnode ./sample.jsで出来ます。
+
+```
+#1
+#3
+#2
+```
+
+プログラムは上から順に実行されます。
+同期処理では、一つ前の実行が終わるまで次の実行は待ちます。
+非同期処理では、一つ前の実行に時間がかかる場合、実行完了をまたずに次の処理が行われます。
+
+ざっくり言うと、前の反応を待つのが同期、前の反応を待たないのが非同期です。
+
+JavaScriptは非同期で処理が実行されるため、上記のサンプルコードでは「#1 #2 #3」という順番ではなく「#1 #3 #2」という結果になったのです。
+
+## Promiseとは何か?
+上記のサンプルコードを上から順番に実行するためにはどうすればいいのでしょうか?
+この問題に対する解がPromiseです
+
+Promiseを使うことで非同期言語のJavaScriptを同期的に書くことが出来ます。
+「非同期処理で使うもの = Promise」ではなく「非同期処理の言語を同期的に扱いたい時に使うオブジェクト = Promise」です。
+
+
+Promiseは以下のメリットを得られます。
+
+- コールバック地獄(もはや死後なので詳細は割愛)を回避できる
+- 時間のかかるAPIから取得した値を次の処理に渡せる
+- 上から順に実行されるので可読性が上がる
+
+### 使い方
+最初のサンプルコードを数字順に表示するコードをPromiseで書いてみます。
+
+```.js
+console.log('#1')
+
+const sample = () => {
+ return new Promise((resolve, reject) => {
+ setTimeout(() => {
+ console.log('#2')
+ resolve()
+ }, 500)
+ })
+}
+
+sample()
+ .then(() => {
+ console.log('#3')
+ })
+ .catch(() => {
+ console.error('error')
+ })
+```
+
+実行すると、順番に実行されます。
+
+```
+#1
+#2
+#3
+```
+
+コードが冗長に感じるかもしれませんが、詳しく見ていきます。
+
+#### Promisオブジェクトの生成
+
+Promiseを使うためには、最初にPromiseコンストラクタをnewして、初期化されたPromsieオブジェクトを生成します。
+コンストラクタ引数は関数をとり、その関数の中に非同期処理を書きます。
+
+```.js
+const sample = () =>
+ // 返り値にPromiseオブジェクトを生成
+ return new Promise((resolve, reject) => {
+ // 非同期で処理したいことを記述
+ // 成功したらresolve()を呼ぶ
+ resolve()
+ // 失敗したらreject()を呼ぶ
+ reject()
+ })
+}
+```
+
+Promiseは3種類の状態を持っています。
+状態によって次に実行されるメソッドが変わります。
+一度Pendingから状態が変化した後に、別の状態になることはありません。
+
+- Pending 初期状態
+- Fulfilled `resolve()`が呼ばれた時
+- Rejected `reject()`が呼ばれた時
+
+#### 次の処理に繋げる
+
+Promiseオブジェクトは、`then()`と`catch()`の2つのメソッドを持っています。
+Promsieの状態がFulfilledの時はthen()が実行され、Rejectedの時はcatch()が実行されます。
+
+```.js
+sample()
+ .then(() => {
+ // sample()でresolve()が実行された後の処理
+ })
+ .catch(() => {
+ // sample()でreject()が実行された後の処理
+ })
+```
+
+ thenのコールバック関数の第一引数には、Promiseでresolveに渡した値が渡ってきます。
+
+一旦ここまでがPromiseの基本的な説明になります。
+
+## Promiseを繋げて使う
+
+APIを叩いて値を取得する`getParam`という関数を作成します。
+Stringを返す架空のAPIを仮定しています。例えば`/sample/foo`を指定するとfooが返ります。
+
+```.js
+const getParam = url => {
+ return new Promsie((resolve, reject) => {
+ const request = new XMLHttpRequest()
+ request.open('GET', url, true)
+
+ request.addEventListener('load', (e) => {
+ if (request.status === 200) {
+ resolve(request.responseText)
+ } else {
+ reject(request.statusText)
+ }
+ })
+
+ request.send()
+ })
+}
+```
+
+Promiseは繋げて書くことが出来ます。
+Promiseチェーンと呼ばれたりします。
+
+```.js
+getParam('/sample/foo')
+ .then(res => {
+ console.log(res)
+ return getParam('/sample/bar')
+ })
+ .then(res => {
+ console.log(res)
+ return getParam('/sample/fuga')
+ })
+ .catch(err => {
+ console.log(err)
+ })
+```
+
+とてもきれいに書けますね!
+実行結果は以下(想定)です。
+
+```
+'foo'
+'bar'
+'fuga'
+```
+
+## 並列処理
+
+Promiseを使っていると複数の非同期処理が成功後に、何かの処理をしたい時が出てきます。
+
+そんな時に使えるのが`Promise.all`です。
+非同期で処理したいものを配列に入れて、全てのPromiseオブジェクトが`fulfilled`になった時にthenが呼ばれます。
+
+```.js
+Promsie.all([
+ getParam('/sample/ringo')
+ getParam('/sample/gorira')
+ getParam('/sample/rappa')
+ getParam('/sample/pantu')
+]).then(res => {
+ console.log(res)
+})
+```
+
+```
+['ringo', 'gorira', 'rappa', 'pantu']
+```
+
+同様に`Promise.race`は引数に配列を受けて、その中のどれか1つが`fulfilled`または`refected`になった時にthenが呼ばれます。
+
+## async/awaitを使う
+
+PromsieチェーンによりJavaScriptはすっきりと 同期的なコードを書くこと可能になりました。
+async/awaitを使うことでさらに分かりやすいコードを書くことが出来ます。
+
+```.js
+const getAsyncData = async id => {
+ try {
+ // idを使ってAPI経由でnameを取得
+ const name = await getParam(`/sample1/${id}`)
+ // nameを取得した後に、nameを使ってbelongsを取得
+ const belongs = await getParam(`/sample1/${name}`)
+ return belongs
+ } catch(err) {
+ throw err
+ }
+}
+```
+
+async/awaitを使うことで、then()やcatch()という記述をする必要がなく、より直感的に同期的な処理を実現出来ます。
+
+ポイントは以下です。
+
+- awaitは、直後に記述されたPromiseオブジェクト内で処理が完了するまで処理を一時停止します。
+- awaitはPromsieの処理完了時に持っている値を取り出し変数への代入を行います。
+- awaitはasyncの中でのみ使えます。
+- 例外処理はtry-catchで行います。
+
+## まとめ
+
+JavaScriptで分かりにくいPromsieについて解説しました。
+JavaScriptはそもそも非同期で処理する言語であり、欠点を補い同期的な処理を行うためにPromsieが使われるということが理解できればPromsieは使いこなせるのではないかと思います。
+
+async/await、PromiseはIE11以外のモダンブラウザでは全て対応しているので、プロジェクトでもどんどん使っていきましょう!
+
+
+
+
+
+
+