See the Elephant

1992生まれのプログラマが書くエンジニアブログ

fetch apiをあらためて 3

前回の振り返り

前回は, fetchの第2引数としてrequestのパラメタを設定する方法について触れた.

Responseオブジェクト

Responseインスタンスは, fetch()プロミスが解決(resolve)された時に返り値として渡される. どんなレスポンスオブジェクトでも共通で使用できるレスポンスプロパティを持つ

  • Response.status
  • Response.statusText
  • Response.ok
    • HTTPステータスが200~299のうちに収まっているかどうかのショートハンド. Booleanを返す

Response()コンストラクタはオプションとして2つの引数をとることができる - レスポンスボディと初期化オブジェクトです(requestが受け取れるものと似ている)

Body

リクエストもレスポンスもボディを持っている bodyは以下のタイプのいずれかのインスタンスである

  • ArrayBuffer
  • ArrayBufferView
  • Blob/File
  • 文字列
  • URLSearchParams
  • FormData

BodyミクスインはRequestやResponseに実装されている コンテンツを抜き出すために以下のメソッドが定義されている
以下は全て最終的に実際の中身で解決されるプロミスを返す

  • arrayBuffer(): 固定長のバイナリデータを持つbufferを完全に読み取る
  • blob(): ファイルに似たオブジェクト. immutableな生データをResponseストリームから完全に読み取る
  • json(): ボディテキストをJsonとして解析した結果で解決されるpromiseを返す
  • text(): textで解決するpromiseを返す. 常にUTF-8でデコードされる
  • formData(): XHR.send()を用いることで簡単に送信が可能なフォームフィールドおよびそれらの値から表現されるキーと値のペアのセットで解決されるpromiseを返す

きになるキーワード

service worker: https://developer.mozilla.org/ja/docs/Web/API/ServiceWorker_API cors: https://developer.mozilla.org/ja/docs/Web/HTTP/HTTP_access_control

fetch apiをあらためて 2

前回のふりかえり

前回 は MDN fetchからfetch, promiseの概要について触れた.

promiseを使うと, 簡潔に非同期処理を記述できる.
fetch apiを使うことでわかりやすくhttp req/respの操作を行える.

今回もMDN fetch api を読んでいこう.

リクエストにオプションを適用する

fetch()メソッドには2つ目の引数を適用することもできます。
多数の設定をコントロールすることのできる初期化オブジェクトです。
// Example POST method implementation:
postData('http://example.com/answer', {answer: 42})
  .then(data => console.log(data)) // JSON from `response.json()` call
  .catch(error => console.error(error))

function postData(url, data) {
  // Default options are marked with *
  return fetch(url, {
    body: JSON.stringify(data), // must match 'Content-Type' header
    cache: 'no-cache', // *default, cache, reload, force-cache, only-if-cached
    credentials: 'same-origin', // include, *omit
    headers: {
      'user-agent': 'Mozilla/4.0 MDN Example',
      'content-type': 'application/json'
    },
    method: 'POST', // *GET, PUT, DELETE, etc.
    mode: 'cors', // no-cors, *same-origin
    redirect: 'follow', // *manual, error
    referrer: 'no-referrer', // *client
  })
  .then(response => response.json()) // parses response to JSON
}

http://example.com/answer に対して {answer: 42} というjsonをpostするコードである. fetchの第2引数にhttp requestの設定を書くことができる.

json形式で記述できるためかなり簡潔で読みやすい. ここに記述されるパラメタはRequest インタフェースのプロパティに対応する.

credentials, mode, redirectについて見ていこう

credentials

リクエストのクレデンシャルを設定する.
クロスオリジンリクエストの場合, UAがほかのドメインからクッキーを送信すべきかどうかを示す.

  • omit : 決してクッキーを送信しない
  • same-origin: URLが呼び出し元のスクリプトと同一オリジンだった場合のみクッキーを送信する
  • include: クロスオリジンの呼び出しであっても常にクッキーを送信する

mode

クロスオリジンリクエストに対して有効なレスポンスができるか,
またレスポンスのプロパティが読み取り可能かどうかを判定するために使用される.

  • same-origin: リクエストが常に同一オリジンに行われることを保証するために使用できる. このモードを設定して他のオリジンにリクエストをした場合, 結果は単純にエラーになる.
  • cors: クロスオリジンリクエストを許可する. 例えば, サードパーティーベンダーが提供する様々なAPIにアクセスできる. これらはCORSプロトコルに乗ることが期待されている.
  • no-cors: HEAD, GET, POST以外のメソッドを防ぐ. 任意のServiceWorkersがこれらをインターセプトする場合, これら

redirect

リダイレクトがどのようにハンドリングされるかを設定する.
デフォルトは follow.

今日はここまで

次に読みたいものリスト

fetch apiをあらためて

fetch

fetchを雰囲気でしか理解できていないので学んでいこう

mdn fetch概説

fetchはjsのAPIであり,リクエストやレスポンスといったHTTPのパイプラインを構成する要素を操作できるようになる.
またfetch() メソッドを利用することで、非同期のネットワーク通信を簡単にわかりやすく記述できるようになります。

うむ, productionコードでもfetchが登場する. XMLHttpRequestとの違いはなんだろう

XMLHttpRequest

XMLHttpRequest は、クライアントとサーバーの間でデータを伝送するための機能をクライアント側で提供する API です。ページ全体を再読み込みすることなく、URL からデータを読み出す簡単な方法を提供します。この API によって、ユーザの作業を中断させることなく Web ページの一部を更新することができます。 XMLHttpRequest は AJAX プログラミングで多く使用されます。

これはfetchも同じそう. apiのメソッドや, 使われ方見た感じでは xhrオブジェクトを生成して, オブジェクトに値をバインドしていき http reqパラメタを作るような感じになるっぽい.

https://qiita.com/sirone/items/412b2a171dccb11e1bb6

var xhr = new XMLHttpRequest();
xhr.open( 'POST', 'http://{送信先URL}/post.php', false );
// POST 送信の場合は Content-Type は固定.
xhr.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded' );
//
xhr.send( 'hoge=piyo&moge=fuga' );

fetch apiを使うことでオブジェクトの定義を行わずにhttp reqが生成できる

var myImage = document.querySelector('img');

fetch('flowers.jpg').then(function(response) {
  return response.blob();
}).then(function(myBlob) {
  var objectURL = URL.createObjectURL(myBlob);
  myImage.src = objectURL;
});

これはネットワークごしに画像を取得して <img> 要素に挿入するスクリプトである. 第一引数は取得したいリソースへのパスであり, 戻り値はResponseオブジェクトを獲得できるプロミスオブジェクトを返す.

fetchで取得したプロミスオブジェクトは function(response) のresponseに渡されてcallback functionで利用できる.

promiseオブジェクトってなんじゃい, ということで調べる.

promise

promiseとは

Promiseは非同期処理の最終的な完了もしくは失敗を表すオブジェクトです。要するに、Promiseはコールバックを関数に渡すかわりにコールバックを付属させるリターンされたオブジェクトです。
// 成功時にsuccessCallback, 失敗時にfailureCallbackが呼ばれる
doSomething().then(successCallback, failureCallback);

promiseを利用することでdoSomethingの処理が終わり, その成功/失敗状態に応じて次に実行するcallback関数を切り替えてくれる.

一般的なニーズとしては、複数の非同期処理を順番に実行し、前の処理の結果を次の処理で使うというものがあります。これはPromiseチェーンを作成することで行えます。

さあ魔法の時間です。 then 関数は元のPromiseとは別の新しいPromiseを返します。
基本的に、それぞれの Promise はチェーン(連鎖)させた別の非同期処理の完了を表します。
Promise は doSomething() の完了を表すだけではなく、渡した successCallback もしくは failureCallback の完了も表し、これらのコールバックはPromiseを返す別の非同期関数であっても構いません。
これまで、複数の非同期処理を順番に実行するには、昔ながらのコールバックの悲運のピラミッドを作る必要がありました。
近代的な関数を使えば、その代わりに戻り値の Promise にコールバックを付加して Promise チェーンとして記述できます。
//従来
doSomething(function(result) {
  doSomethingElse(result, function(newResult) {
    doThirdThing(newResult, function(finalResult) {
      console.log('Got the final result: ' + finalResult);
    }, failureCallback);
  }, failureCallback);
}, failureCallback);

// promise
doSomething().then(function(result) {
  return doSomethingElse(result);
})
.then(function(newResult) { // doSomethingElse(result)の成功時実行結果
  return doThirdThing(newResult);
})
.then(function(finalResult) { // doThirdThing(newResult)の成功時実行結果
  console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback); // promiseが失敗した時の処理

promiseを使うことで非同期処理のチェーンを簡潔に記述できる

fetchのサンプルコードを読んで見る

var myImage = document.querySelector('img');

fetch('flowers.jpg').then(function(response) {
  return response.blob();
}).then(function(myBlob) {
  var objectURL = URL.createObjectURL(myBlob);
  myImage.src = objectURL;
});

ここまで調べて見ると,

  1. fetchメソッドで'flowers.jpg'にxhr get request
  2. responseオブジェクトがpromiseで返却
  3. response.blobでrequestストリーム全文読み込みを行いblobをpromiseで返却
  4. blobからURLを取得し, myImageに設定

promiseとfetchを組み合わせることでxhr requestがかなりいい感じで記述されていることがわかった 今日はここまで

ふりかえり

たまには自分を振り返る時間があってもいい 最近仕事でもやもやすることがおおい. 頭をダンプしてみよう

ここ一年, プログラマをやってみてどうだろう 何もできないと無力感を感じることが多い 自分はチームに不要なのではと思うこともしばしある. これはチームが求めるスピードや周りのエンジニアとの比較によって思う部分が大きい.

とはいえ, 自分が作ったものや自分が考え出したものによって 少なからずお金が生み出されていっているのは尊い事実である. そして何よりも, 昔は自分にはできないと思っていた技術で今は飯を食っていることがなによりも進化である あと, 何も進んでいないわけでもない

自分が本格的に自分の力で技術を学び始めたのはB4の終わりだったと思う http://namu-r21.hatenablog.com/entry/2015/02/18/175958

ブログを見る限りでは3年前の今頃である. この3年を振りかえってみてどうだろうか.

2015年終わりといえば実務訓練が終わって帰ってきた頃合いだろう. そのころになってやっと自分の手でコードを書く楽しみがわかってきた時期だと思う

実務で書いたC++のコードは今考えれば完全にクソだったし ほぼ指導もないままにものとして動くまで作ったのもまあまあできたものだと思う.

実務で得たのは, 自分がコードを早く生み出せない事実と 英語が読めなければ & 自分で調べることができなければ エンジニアとしてものが生み出せない事実である.

今思えば, その気づきやそこから自分で手を動かして学んだことが 今につながってきている

修士は研究はほどほどにネットワーク作ったり, Railsでアプリを書いたりしてた記憶がある それはどれもインターンがきっかけだったように思う

B4実務インターンでは1人進捗が出せず唸って職場で寝たり(これは今思っても最低), M1夏のインターンでは同世代がバリバリweb appを作り(自分はweb初経験), M2冬のインターンでは同級生に助けられてばかりだったり,

インターンでは散々悔しい思いをし, それを糧に技術を勉強してきた webを勉強してアプリを作り始めたのはM1の終わり(多分2016年2月)だからちょうど2年くらいか

そこから大学祭のメンバーと開発したり, 内定者開発やったり... 今は, 2年前の自分なら確実にできていないことができるようになってきている

やっぱりできていないことは多いし, 悔しい思いをすることも多い. でもそれはこれまでのようにできなかった自分を知れただけ.

わからないことは多々あるが日々の中で自分でしっかりと理解, 消化していくことが何よりも大事 毎日少しづつ前に進んでいこう

Doctrine データ永続化のタイミングとUnit of Work

最近は業務でsymfonyを利用している. symfonyが標準で利用するORM Doctrineの永続化周りについて少し学んだのでメモ.

日本語のこの記事が分かりやすかった.

ja.stackoverflow.com

Doctrineのデータ永続化のタイミング

そもそも Doctrineとは

いわゆる ORM.

詳しくはこちら :
Home — Doctrine Project

Doctrineを利用することで, DBのTableとSymfonyのEntity間でデータのマッピングを行うことができる. SymfonyのEntityに @ORM\ Annotationでデータマッピング設定を書くことでDoctrineがマッピングを行ってくれる. 素敵.


Entity Managerのお仕事

詳しくはこちら :
Databases and the Doctrine ORM (current)

DoctrineではEntity Managerと呼ばれるマネージャで, objectの状態管理を行う. ここでのobjectはDBの1レコードを表しているという理解. DoctrineからDBに対する操作を行う際, 必ずこのマネージャにobjectを登録する必要がある.

Entity Managerへの登録操作が persist($obj) メソッド. persist(永続化)と行っておきながらこの時, DBに対するsqlは発行されない.

実際のDBへのSQL発行(永続化)は flush() メソッドによって行われる. このメソッドが呼ばれた時に, 実際のクエリが発行される.

7. Working with Objects — Doctrine 2 ORM 2 documentation

7.3 Persisting entities

Invoking the persist method on an entity does NOT cause an immediate SQL INSERT to be issued on the database.
Doctrine applies a strategy called “transactional write-behind”, which means that it will delay most SQL commands until EntityManager#flush() is invoked which will then issue all necessary SQL statements to synchronize your objects with the database in the most efficient way and a single, short transaction, taking care of maintaining referential integrity.

An entity can be made persistent by passing it to the EntityManager#persist($entity) method. 
By applying the persist operation on some entity, that entity becomes MANAGED, which means that its persistence is from now on managed by an EntityManager. 
As a result the persistent state of such an entity will subsequently be properly synchronized with the database when EntityManager#flush() is invoked.

Unit of Workのライフサイクル

DoctrineではUnit Of Workという考え方を利用している.

http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/working-with-objects.html#working-with-objects

これは, objectレベルのトランザクションのようなもの, と表現されている.

自分の中では, Entityに対する一連の変更を Unit of Workという単位で保持しておき, commit時に実際にDBに適応する, という理解.

Unit of Work は, EntityManagerが初期作成された時, もしくは flush()が実行された時に生成される.

flush() が実行された時にDBに対する反映が行われる. flush()が呼び出されない限りは, persistremove(objectの削除)といったUnit of Workに記録されたEntityに関する操作はDBに反映されない.

flush()されずに, close()や新たなEntityManagerが生成された場合はUnit of Workに記録された一連の変更を破棄する.

ちょっとかしこくなった

mac sierra, tmux で '~/.tmux/plugins/tpm/tpm' returned 127 (pluginがうまく動かない)

tmuxのpluginがうまく動かない

これを動かしたかった.

tech.quartetcom.co.jp

僕の.tmux.conf はこんなの

一部抜粋

# tmuxのセッションを保存する
# prefix C-s : セッション保存
# prefix C-r : セッション復元
# see : http://tech.quartetcom.co.jp/2016/01/15/tmux-continuum/
set -g @tpm_plugins 'tmux-plugins/tpm'
set -g @tpm_plugins 'tmux-plugins/tmux-resurrect'
set -g @tpm_plugins 'tmux-plugins/tmux-sensible'

# resurrect
set -g @resurrect-strategy-vim 'session'
set -g @resurrect-processes 'irb pry "~rails server" "~rails console" mysql ssh php'

# continuum
set -g @continuum-restore 'on'

# tmux内でopenコマンドが使えるようにする
# これをやらないとうまく動かない
# brew install reattach-to-user-namespace

set -g default-command "reattach-to-user-namespace -l ${SHELL}"

run-shell '~/.tmux/plugins/tpm/tpm'

<prefix>r でconfファイルをリロードすると以下のエラーが出る

'~/.tmux/plugins/tpm/tpm' returned 127

本家のissue を見るとどうやら ElCapitanあたりで行われたpermission変更の影響を受けているらしい.

I get error 127 when trying to install. · Issue #67 · tmux-plugins/tpm · GitHub

このコメントによると

I get error 127 when trying to install. · Issue #67 · tmux-plugins/tpm · GitHub

システムのプロテクション切ると使えるとのこと. これは辛いのでやめておこう.