Promiseとは その3 非同期通信
Promise:
Promise内で非同期通信の仕方
Promiseを順に読んだあと、Ajaxを読むと一撃でわかるよ。 Ajax
ただただPromise
オブジェクトのなかにxhr = XMLHttpRequest()
インスタンスを作成して通信させればできちゃいます。
function getUrl(url) { //Promiseインスタンスを作成して返す return new Promise((resolve, reject) => { //非同期通信するためにXMLHttpRequestのインスタンスを作成 let xhr = XMLHttpRequest(); xhr.open("GET", url); //リクエストが完了した時 xhr.onload = () => { //statusコード確認 if (xhr.status === 200) { //resolveの場合の処理 resolve(xhr.response); } else { //rejectの場合の処理 reject(new Error(xhr.statusText)); } }; //リクエストが失敗した時 xhr.onerror = () => { reject(new Error(xhr.statusText)); }; //通信開始 xhr.send(); }); } function getFirstItem() { //getUrl("/items")でデータ取得後、thenのitemsに取得したデータが渡される。 return getUrl("/items").then((items) => { //取得した情報をもとにURL作成 return getUrl("/items/" + items[0].id); }); } getFirstItem().then((item) => { // 成功時の処理(item); }).catch((e) => { //エラー処理 console.error(e); });
ここでcatch((e) => {})を利用しているが、これはthen(null, (e) => {});と同義。 then()のチェーンの最後に付けておけば、一連の処理中にreject()で明示的に処理失敗を宣言した場合だけでなく、予想外のエラーも拾ってくれる。
Promiseとは その2
Promise: 【第二弾 Promiseとは】 ・「Promise.all」で複数のPromiseを「並行処理」する方法こともできます。 非同期処理を配列に複数格納して、配列内のすべての非同期処理が完了したら.thenを実行するということができる。 Promise.all([ 非同期処理1(resolve('非同期処理1終了')), 非同期処理2(resolve('非同期処理2終了')), 非同期処理3(resolve('非同期処理3終了')) ]).then(function(data) { // ここのdataには配列が格納されます。 console.log(data); console.log('すべての処理が終了しました!'); }) //下記のような結果が出力される ["非同期処理1終了 ", "非同期処理2終了", "非同期処理3終了"] すべての処理が終了しました! ・エラー並行処理中にエラーが発生した場合 Promise.all([ 非同期処理1(resolve('非同期処理1終了')), 非同期処理2(reject('error')),//エラーになった場合 非同期処理3(resolve('非同期処理3終了')) ]).then(function(data) { console.log(data); console.log('すべての処理が終了しました!'); }) rejectでerrorとなっているので.thenのメソッドは実行されません。注意が必要です。 ・Promise.race()は複数のPromiseを「並行処理」するが、並行処理のなかで「resolve」または「reject」が呼び出されたら「.then」の処理が実行されます。 Promise.race([ 非同期処理1(resolve('非同期処理1終了')), 非同期処理2(reject('error')),//エラーになった場合 非同期処理3(resolve('非同期処理3終了')) ]).then(function(data) { // ここのdataには配列が格納されます。 console.log(data); console.log('すべての処理が終了しました!'); }) //allのときとは違い、.thenが実行されました。 ["非同期処理1終了 ", "error", "非同期処理3終了"] すべての処理が終了しました! このような違いがあるので注意です。
Promiseとは その1
Promise: 【第一弾 Promiseとは】あとでPromise.allなど非同期処理を並列で行うこととか別で書きます。 【async/awaitは更にPromiseを可読性よく書くようにしたもの】ですのでそのもとを知るとわかりやすいとおもいました! まずJSには同期処理と非同期処理があります。 ・同期処理とは、上から順番に処理を実行していくことです。 関数A(同期処理) ↓ 関数B(同期処理) ↓ 関数C(同期処理) だけどこの一連の処理が完了させる場合、「関数B」の処理がクソ重くて動かなかった場合、「関数C」はただただぼけーーーっと関数Cの順番がくるのを待っているだけで何も実行されない。 これが同期処理です。 ・非同期処理とは、上から順番に処理を実行しつつ非同期処理に結果が返ってくるのを待たずに次の処理を実行するという仕組み。 A関数(同期処理) ↓ B関数(非同期処理) ↓ C関数(同期処理) 「B関数」の処理がクソ重くても非同期処理にしてあるから、先にC関数実行していいよという流れです。 ・非同期処理の結果が返ってきたあとどうするのか。 関数A(日付データを取得する) ↓ 関数B(日付データから西暦を抽出する) //関数A 日付データを取得する function getDate() { var date = new Date; } //関数B 日付データを元に西暦を取得する function getYear(data) { var year = data.getFullYear(); } 日付データ取得後に、取得したデータをもとにgetYear()(西暦を計算する関数)を実行。なので日付データ取得がうまくいかないとgetYear()は実行できない状態です。 なのでこのgetDate()の処理が成功したあとにgetYear()を実行するようにcallbackを使って書き直して見る。 //getDateの引数にcallback(名前はcallbackでなくてもいい)という関数を引数に受け取れる用に変更してみました。 function getDate(callback) { callback(new Date); } //getDateの引数に関数を渡すと下記のように変化します。 getDate(function(data) { //getYear(data)のdataにはcallback(new Date)のnew Dateの値が入ってくる getYear(data); }); それならコールバックの連鎖つくれるやんとなって下記のようになる(これはコールバック地獄で処理をおうのが大変でクソコード認定される) getDate(function(data1) { getYear(function(data2) { getSomething(function(data3) { getAnotherThing(function(data4) { //何らかの処理をする }); }); }); }); このコールバック地獄を解消してくれるのがPromiseです! PromiseはPromiseオブジェクトを作成して簡潔に非同期処理がかけるというもので、Promiseオブジェクト作成は下記の様に書きます。 ・pending(待機中)/resolve(成功時の処理)/reject(失敗時の処理) //new Promise( )の引数に関数を設定するのが一般的らしい。 var result = new Promise()//これがプロミスオブジェクトの作成!! var result = new Promise(function(resolve, reject) { //成功時の下記を実行 resolve(new Date); //失敗時に下記を実行 reject(new Error('Error')); }) このあと非同期処理をおこなうようにするには.thenを使った処理を書きます。 //function(data)にはresultで返ってきた値が格納されます。 result.then(function(data){ console.log(data.getFullYear()); ).catch(function (error) { // rejectが実行された場合catchが呼ばれエラーが返される console.log(error); // => 'Error' }); } thenをつなげてかくことによって連鎖(メソッドチェーン)した非同期処理をかくことができる。 //thenは返り値を明確に書いてあげないとだめです。「return 返したいPromiseオブジェクト」を関数内にちゃんと書いてあげましょう。 function something1() { //「return 返したいPromiseオブジェクト」 return new Promise(function(resolve) { setTimeout(function() { resolve('なにか処理'); }, 3000) }) } //処理をつなげるとこんな風に書きます。 result.then(関数A).then(関数B).then(関数C).catch(function (error) { console.log(error); }); 【ES6】だとこんなふうに書きます。 result.then( (data) => console.log(data.getFullYear()) );
Ajaxとは
Ajax: 非同期通信の仕方 ajaxは多分jqueryをつかってとか、railsにあるajaxのやり方でやってきたとおもうのですけど、今一度仕組みを理解しておこう。 それぞれの書き方は別途調べてつかってください!基本的な情報をここにはかいておきます。 サーバーと非同期で通信するために「XMLHttpRequest()」というAPIがあるのでそのインスタンスを作成して使用しましょう! var xhr = new XMLHttpRequest(); // responseTypeを予め指定しておくとあとで「JSON.parse」しなくてよいので任意のresponseTypeを設定しておこう。 xmlHttpRequest.responseType = 'json'; //xhr.open(【どんな方法で?】, 【どのサーバーに?】); xhr.open('GET', 'https://furien/api'); //send()で通信開始! xhr.send(); //onreadystatechangeでサーバーからのデータを受信します! xhr.onreadystatechange = function() { //通信状態を確認できる。通信状態によって行いたい処理がif文で条件分岐できるよ! //4ならデータ取得完了!という意味 if ( xhr.readyState === 4 ) { //データ取得処理を書く } } ・データを受信するのはいいけど、通信が成功してるのか?通信はじまってるか?など詳細がわからないので「.readyState」で通信状態を管理することができます。 0 準備段階 まだ通信は行われていない状態 1 準備完了 通信を行う準備が完了している状態 2 通信開始 サーバーと通信が始まっている状態 3 受信中 サーバーから目的のデータを取得している状態 4 通信完了 データを取得して通信が終了している状態 ・statusをつかってエラーハンドリングする。(status 200とかみたことあるはず) ・「.onreadystatechange」の中で xhr.statusをつかって通信結果が確認できる。 xhr.onreadystatechange = function() { //通信が正常な場合の処理 if( xhr.status === 200 ) { //データ取得処理を書く } }; ・他にもあるけどよく見るやつ 200 成功 特に問題なく通信が成功した状態 401 エラー 認証が必要なため通信できない状態 403 エラー アクセスが禁止されていて通信できない状態 404 エラー 情報が存在しないために通信できない状態 500 エラー サーバー側の不具合で通信できない状態 503 エラー サーバーに負荷がかかって通信できない状態 xhr.onreadystatechange()と同じようにつかえる他のメソッドもあるのでケースバイケースで使い分けましょう。 ステータスに名前がついているようなものなので、下記のほうがわかりやすいかも!! onloadstart : リクエストが開始した時 onprogress : リクエストが完了するまで周期的に実行される。受信したデータを確認することも可能。 onabort : abortメソッドによってリクエストが中断された時 onerror : リクエストでエラーが発生した時 onload : リクエストが完了した時 ontimeout : リクエストがタイムアウトした時 ・GET通信 xhr.open('GET', 'http://sample.com/test.php?q=hogehoge');というようにかきます。 検索とかした時に xhr.open('GET', 'http://sample.com/test.php?q=hogehoge'); のようにURLと続けてサーバーに送信したい情報を「?q=hogehoge」のようにつけて書きます。 ・POST通信(基本的に書き方はGET通信とかわらないです) ・.setRequestHeader()をつけて「content-type」を指定してあげる必要があります。指定しないとうまくデータがとれないことがあります。 ・.send() GETのときにURLに続けて送信したかった情報を、send()内に書いて送信します。 var xhr = new XMLHttpRequest(); xhr.open('POST', 'http://sample.com'); xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded;charset=UTF-8'); xhr.send( 'q=hogehoge' );
継承チェーン
継承チェーンとは、そのオブジェクトがなんのオブジェクトを継承しているかのこと。
Rubyではオブジェクトの大元はBasicObject
というオブジェクトです。継承しているオブジェクトをたどっていくと行き着く先はBasicObject
になります。
その過程で継承しているクラスが様々なメソッドをもっていて、それらを僕たちが使っているというイメージです。
コンソールでirbをつかって確認 # Stringのスーパークラスを確認 String.superclass => Object # StringのスーパークラスであるObjectのスーパークラスを確認 Object.superclass => BasicObject # BasicObjectのスーパークラスを確認するがnilとなるのでここが大元となる BasicObject.superclass => nil
これでスーパークラスが何かをたどることができました。 あのメソッドを使いたいが、どのクラスがもっていたかや、このクラスにあるはずのメソッドが使えないときに、継承もとをたどるとミスや勘違いを発見することができるかもです。
2.継承チェーン上のモジュールについて
ancestors
メソッドを使うと親クラスすべてが確認できます。
String.ancestors => [String, Comparable, Object, Kernel, BasicObject] 「Comparable」と「Kernel」はさっきのsuperclassにはなかったが、この2つはモジュールです。 ・ KernelはObjectクラスにインクルードされていて、ComparableはStringにインクルードされています。
- モジュールとクラスの読み込みのタイミング
module A end module B end # クラスC に モジュールAとBをincludeする class C include A include B end p C.ancestors [C, B, A, Object, Kernel, BasicObject] というように、クラスCが読み込まれたあとに、モジュールを下から順に読み込んでいます。 なので、モジュールBがモジュールAのメソッドを使おうとすると読み込む順番的に method_missing が発生します。 # prependメソッドを使用すると読み込む順番が変わる module A end module B end class C prepend A #includeをprependに変更 include B end p C.ancestors [A, C, B, Object, Kernel, BasicObject] となり、クラスよりも先にモジュールAが読み込まれる順に変わりました。 先に予め読み込ませて置くのを『preload』ともいうので覚えておくといいかも!!
【読書】効率の良いインプット方法
インプット・アプトの効率を上げるには
勉強や読書などしたがすぐに忘れてしまわないために。
インプットとアウトプットは『 3 : 7 』のバランスが良い
重要なことはインプットをしたら必ずアウトプットを行なうこと。
アウトプットは、誰かに話しても良いし、ブログに書き出しても良い。SNSに投稿してもいい。
とにかく、学んだことを書く。そして疑問も書いておく。
インプットをするための本や教材の難易度を調整しよう
新しいことを勉強する際に気合をいれて『すごく分厚い難しい専門書』に手をだすのはやめよう。
自分にとって急にレベルの高い教材に手を出したくなるのはわかるが、内容の理解が専門的で読み解けない(読むのに非常に時間がかかる)
レベルが高いが故に、前提知識がないと理解できず読んでる意味がない(理解できないのでなにも定着しない)
初めて取り組む分野は、世間的に評判の良い『初心者向け』の本から着手しよう。
アウトプットは2週間に3回以上しよう
2週間で3回以上のアウトプットをするとアウトプットしたことが長期記憶の範囲へ脳の中で移行されるらしいのでアウトプットをしよう。
インプットをするときは、他人に教えることを前提に勉強や読書する
他人にインプットしたことを話した相手に有益な情報になるような気持ち
でインプットすることが大切。
ここで勘違いしてはいけないことは話した相手に嫌な気持ちにさせないように配慮することも大切。
知識や知ったことに興味がなさそうなときは、お互いの趣味や日常の話に切り替えたりと話す相手にも配慮しよう。
そしてマウントを取って知識などを教えずひけらかすなどもっての他なので気をつけよう、小さなプライドなど捨てよう。
こういった心構えで(緊張感をもって)勉強などしてみることがおすすめ。
疑問や考えをメモしておこう
小説などは置いといて、勉強やビジネス書籍を読む際は、付箋や小さなノートを用意して考えと疑問
をメモしておこう。
例えば、
マーケティングの本を読んだとき、自分の会社に当てはめて考えたことで、『得られた気づきを書き留めておこう』
気づきや考えを上司や同僚に話す提案材料や検証前の仮説材料としてためておこう。
そして積極的に職場で話そう。間違っていても続けていくうちに成功にたどり着けると僕は考え信じている。
インプットをするときはインプットする目的を明確にしておこう
なぜ、この勉強をするのかを明確にして取り組もう。
NGな例
なんとなく英語勉強しよう→ゴールが設定されていない為、いつまでにどのくらい勉強が必要なのか逆算できない。
OKな例
来年の昇格にtoiec800点必要→ゴールが設定されていて、勉強時間と量が逆洗できる。
※とはいえ、なんでも明確にゴールが設定できるとは限らないので、近場に同じような境遇で、なおかつ自分の目指している資格などを保有している人に参考までに聞いてみよう。
それでも難しければ、ゴールまで仮設計をして一週間・一ヶ月のスパンで自分の習得率を計算し、そこから習得できる期間を割り出してみよう。
- 作者:樺沢紫苑
- 発売日: 2019/08/03
- メディア: 単行本(ソフトカバー)
【Ruby】特異クラスってなに?シンタクスシュガーって?
特異クラスってなに?シンタクスシュガーって?
最近Railsで開発をしているときに、恐らく僕の師匠が書いたであろうコードを見つけた。
class Hogehoge class << self def call(*args) new(*args).call end end end
こんな感じのコードだったんだけど、こんな短いのにまじでわかんなかったw
ああ、これがRailsばっかやってて生のRuby全然やってない負債ってやつかw って思ったら急にやる気(焦り)がでてきて調べてみた。
上のコードは特異クラスっていうやつらしい
特異クラス
クラスメソッドの定義の仕方には大きくわけて2種類
特異メソッド方式
特異クラスによるクラスメソッドの定義例 class Hoge def Hoge.クラスメソッド名1 end def Hoge.クラスメソッド名2 end メソッドを定義するときにメソッド名の前にクラス名を書いて定義する end
特異クラス方式
特異クラスによるクラスメソッドの定義例 class Hoge class << self def クラスメソッド名1 end def クラスメソッド名2 end いくつ定義してもクラスメソッド名の前にクラス名をかかなくていいから便利 end end
クラスメソッドとインスタンスメソッドはなにが違うの?
文系4大卒、プログラマー歴半年未満なのでミリも気にしたことなかったですw
なのでこの際簡単に調べました。奥深くまでは行きません。
クラスメソッド
まあクラスから呼び出せるねん、、、、
そのクラスから作成したインスタンスからそのクラスメソッドは呼び出せないねん、そやねん
インスタンスメソッド
まあクラスから生成したインスタンスから呼び出せるねん、、、、
クラスから作成したインスタンスからそのクラスのクラスメソッドは呼び出せないねん、、、 インスタンスメソッドだけやねん、、、そやねん。
class Hoge # インスタンスメソッドの定義 => いつも書いてるやつ 『hoge』っていうインスタンメソッドを定義 def hoge p "インスタンスメソッド呼び出し成功" end # クラスメソッドの定義 => 上で説明したやつ 『fuga』っていうクラスメソッドを定義 def self.fuga p "クラスメソッド呼び出し成功" end end # インスタンスメソッドの呼び出し Hogeクラスからインスタンスを生成! Hoge.new.hoge 結果:"インスタンスメソッド呼び出し成功" って表示される。呼び出し成功 #インスタンスメソッドの呼び出しに失敗する クラスからインスタンスメソッドは呼び出せませんなあ〜(・8・) begin Hoge.hoge rescue p "インスタンスメソッド呼び出し失敗" end #クラスメソッドの呼び出し Hoge.fuga 結果:"クラスメソッド呼び出し成功" って表示される。呼び出し成功( ・8・ ) #クラスメソッドの呼び出しに失敗する インスタンスからクラスメソッドは呼び出せませんなあ〜(・8・)(・8・)(・8・) begin Hoge.new.fuga rescue p "クラスメソッド呼び出し失敗" end
こんな感じで使い方が違うみたい。
じゃあ、このクラスメソッドとインスタンスメソッドはどう使い分けるの?
という疑問は次回書きます。
次回
クラスメソッドとインスタンスメソッドはどう使い分けるの?
クラスとモジュールってどう使い分けるの?
オブジェクトを「クラス」から作り出すクラスベースのオブジェクト指向とオブジェクトを「オブジェクト」から作り出すインスタンスベースのオブジェクト指向、、、、、????(これは、、、、時間かかるやつだ。)
あ、シンタックスシュガーわすれてたw
シンタックスシュガーとは、『プログラミング言語において、既存の構文などを別の構文や記法で記述できるようにしたもの。 長い構文を簡略に記述できるようにしたり、複雑な構文を見やすくするために用意されることが多い。』
はい。ぐぐったら1番上に出てきました。。。w(・8・)
三項演算子とか配列作るときの『%w』とかもシンタックスシュガーって言われるらしい。
まあ、なんか、簡単にかけるようにしたもんかってくらいの認識でいいかな笑
疲れてのでこのあたりでオシマイにします。
この記事のトピックで良い記事があったら教えてくださいませ。