バンビのブログ

駆け出しのエンジニアです!日々の疑問など備忘録として書いていきます。

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になります。 その過程で継承しているクラスが様々なメソッドをもっていて、それらを僕たちが使っているというイメージです。

  1. スーパークラスのたどり方 superclassメソッドでスーパークラスを確認できる。
コンソールで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にインクルードされています
  1. モジュールとクラスの読み込みのタイミング
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』とかもシンタックスシュガーって言われるらしい。

まあ、なんか、簡単にかけるようにしたもんかってくらいの認識でいいかな笑

疲れてのでこのあたりでオシマイにします。

この記事のトピックで良い記事があったら教えてくださいませ。