質問: p1.then(function(resolve){ console.log(inside_promise) //2はこの部分です。 }) console.log(inside_any + most_inside + inside_promise) //3はこの部分です。 } ``` このような使い方は正しいのでしょうか? 現在どのように変数の受け渡しが可能か手探りでやっていますので、至らない質問かもしれませんが、教えていただけると助かります。 また、このような使い方以外が必要な場合、どのような処理で対応していますでしょうか?
回答: > > やはり、非同期処理の嫌なところは書いた順番に処理し終えて行ってくれないため、それを回避するためにpromiseを使いたいですよね。 > > > 非同期処理の根本の問題はPromiseを使おうが何しようが変わりません。 Promiseを使うメリットは一重にコードのネストを減らしたいが為に行うものです。 > > resolve,rejectってなんのためにあるの? > > > 従来の非同期処理は、予め後続の処理を関数に閉じ込めて引数として渡します。 概ねこんな感じになるでしょう。 ``` const client = require('何かしらのクライアント'); client.get(url, function(err, code, body){ if (err) { console.error(err); return; } if (code != 200) { console.error('ステータスコードが正常ではありません'); return; } bodyを解析 }); ``` 慣習としてコールバック引数は最終引数であること、 コールバック引数として渡した関数の第一引数はerrであるというルールが存在します。 何故か?処理が正常終了したか異常終了したかが見分けられないからです。 ``` const client = require('何かしらのクライアント'); // このtry-catchは無駄 try { client.get(url, function(err, code, body){ if (err) { throw err; } if (code != 200) { throw new Error('ステータスコードが正常ではありません'); } // 省略 }); } catch (e) { console.error(e); } ``` 基本的にJSの非同期処理は関数を引数として渡してあげて、 「終わったらこれ実行しといてね?あとはよろしく!」といった形で正常終了します。 try-catchで受け取れそうな気がしますが、見事スルーされてしまいます。 つまり、非同期処理の場合はこの第一引数を見張り続ける必要があります。 長く深いネスト構造はイライラの元です。 しかもひたすらネストされ続けるのでイライラがMAXになるわけです。 promiseはこの問題にメスを入れました。 失敗時には何時でもrejectを叩いて逃げられます。 また慣習である第一引数のerrがなくなり、コードも簡素になります。 利用者側の視点ではこのようにバリデートと処理を分離することも可能になりました。 ``` const client = require('何かしらのクライアント + promise'); client .get(url) .then(function(code, body) { if (code != 200) { return Promise.reject('ステータスコードが正常ではありません'); } return body; }) .then(function(body) { // 処理 }) .catch(function(err) { console.error(err); }); ``` これがpromiseのrejectの威力で、 見事try-catchに似たような事が出来ることが確認できます。 コードのネストもこれ以上に深くなる事はまずありません。 使う側になって初めてメリットが見えてきますね。 client.getはPromiseを返す関数になっています。 内部で失敗した場合「第一引数にerrを突っ込む」という慣習から解き放たれている事がわかります。 更に後続の処理で`Promise.reject()`でcatchに合流させる事も簡単に出来ます。 > > 以下の三つの1、2、3それぞれの出力はどうなりますでしょうか。 > > > 考え方のヒントですが、JavaScriptは関数の度にスコープを作ります。 関数がネストされる度に、親子関係のスコープが作られます。 子供の中身で変数が定義されていない場合、親のスコープで変数を探します、それでも無ければ更に親の...と辿っていき、グローバル領域で定義されていなければ変数未定義エラーとなります。 因みに1は単なる関数定義なので発火しません。 `(function(){ '処理' })()`のように括弧で包んだ後に`()`で実行してください。 `var most_inside = "Hi"`は関数で包んだ中で実行しています。 関数で作られたスコープはお外に出てくる際になくなり、そのスコープ内で定義していたmost_insideもなくなります。 もし関数を`(fn)()`という即時実行関数に変更して無理やり動作させた場合、 1の出力結果はoutside + inside_anyになるでしょう(そもそもmost_insideの変数未定義エラーで落ちますが) p1とp2はpromiseの定義ですね。(maisumakunさんの仰るように変ですが) thenで繋いでも、関数同士のスコープが共有されるわけではありません。 従って2も変数未定義エラーで落ちます。 3の時点ですが、p1とp2はpromiseの定義を行うだけで素通りするので、 Promiseの実行より前に実行されるでしょう。 まぁそれとは別にmost_inside, inside_promiseの両方が未定義エラーで落ちます。 理由は1や2と同じく関数のスコープの問題です。