株式会社フィードフォースを2018年いっぱいで退職いたします

feedforceアドベントカレンダー8日目! まさかの退職エントリで参加です! adventar.org

7日目はマスタカさんの健康的な記事でした! 私も最近ジムサボりがちなので、そろそろ再開したいですね...

masutaka.net

なんでフィードフォースに来たの?

2009年9月。web2.0ブームの頃にweb業界にあこがれて、SIerから株式会社フィードフォースに転職しました

フィードフォースで何やってたの?

約9年間、サテライトサイト構築サービスと、データフィード運用サービス のエンジニアをやっていました

Railsエンジニアとして入社しましたが、サテライトサイト構築サービスでも物理サーバと格闘したり、データフィード運用サービスでも lambda や ECS をいじったりと、必要に応じていろいろやっていました

個人的には「バッチエンジニア」という呼び名が一番しっくりくるのかなと思っています

最終的には、技術をバリバリに突き詰めると言うよりは、所属するプロジェクトのエンジニアチームをとりまとめたり、カンバンをつくるなどの業務改善をやりながら、コードも書くといった立ち位置でやっていました

9年間務めてどうでした?

KPTで

Keep

  • ボンクラSIerだった自分をいっぱしのエンジニアに育てて頂いたことには感謝しかありません...(本当に入社当初はポンコツでした...)
  • Ruby/Railsだけではなく、サーバの運用にも関われていろいろな経験が積めた(オンプレからECSまで!)
  • 若手からベテランまで、やる気にあふれて優秀な方が多く、本当にいろいろな刺激をいただきました...! 他の会社だったら多分ここまで自分は成長できなかったと思っています
  • 2回ほど社内の賞もいただきましたし、長く居た程度には社内に貢献できた...と思われていればよいな...

f:id:kasei_san:20170608194402j:plain
写真は賞をやり取りしてたのしそうなおじさんたち

Problem

  • 最初数年がボンクラすぎました。スキルと言うより、自分の欠点や素質をまっすぐ見ることが出来なかったのが原因でした
  • 9年は居すぎたかな...と思っている
    • その分得るものも沢山ありました
    • 本当にみなさんステキな方が多くて、今後もプライベートでも仲良くやれそうな人も沢山できた
  • Railsエンジニアではない
    • 自分がその環境を望まなかったのが原因でした
    • viewやフロントエンド、DB関係の知識が足りず、バランスが悪い

Try

  • Railsを一通りできるようにしたい
  • 新しい環境で、今まで得たものを放出しつつ、新しいものを得ていきたい
  • 退職後おちついたらフィードフォースボドゲ部 に遊び行く

どうして退職したの?

上記にもあるように、「9年は長く居すぎた」と「Railsの経験を一通り積みたい」が大きいです

新しい環境を作らないと新しい物を得ることもできないし、こちらが得たものも放出できないとも考えており、 feedforce在籍中にも、副業で他社スタートアップを手伝ったりして、よりその考えがより深まりました

今後は、新しい環境に積極的にTryして自分を高めつつ、新しい環境で得た経験で、そこで価値を提供できるようになれたらいいなぁ...と考えています

安住しやすい性格なので、本当にそこを意識しないと、安住しているうちに一生が終わりそう

つぎどこいくの?

株式会社LCLに転職します

www.lclco.com

日本最大級の高速バス検索サイトを運営しています BtoCも未経験なので楽しみ!

www.bushikaku.net

こちらで、Railsを一通りできるようになりつつ、インフラ部分で貢献していきます!

ほしいものリスト

退職エントリといえば...的な感なので上げてみます

こちらになります

最後に会社のみなさんへ

twitterや、このblogで好き勝手に発信していると思いますので、いつでも気軽にお声がけくださいー

本当にいろいろおせわになりました!

フィードフォースではエンジニアを募集しております!

レベル高くてやる気にあふれた人が沢山います。残業も殆ど無いし、週一リモートワークもチームによってOKなのでぜひ!! (あと、そこまでバリバリしたベンチャー感がなく、「普通の会社」なのでそこも個人的には好きでした)

www.wantedly.com

Amazon Auroraへの移行事例をまとめて知見を抽出してみた

先に知見まとめ

急いでる人はここだけ読めば良いと思う

移行方法

以下の3つの方法のどれかで移行している

  1. RDSの場合、Auroraリードレプリカを作成して、同期完了後、昇格
  2. Amazon DMSでAuroraにデータを移行。同期完了後、切替
    • DMSにはいくつか制限があるので、単純に移行はできない
  3. 自前でAuroraのmasterにレプリケーションを貼る。同期完了後、切替
    • 切替は設定ファイルのエンドポイントを書き換える or DNSのレコード変更

レプリケーションやDMSを使う場合、ついでにテーブルのリファクタリングをしている会社も多い

RDSなら、リードレプリカで移行するのがシンプル
それ以外からの移行 or テーブルのリファクタリングもしたいならば、DMSを使うのがベターか

やったほうが良さそうなこと

  • 段階的な移行として、旧DBとAuroraの同期が完了後、読み込みだけAuroraにする
    • 確かにその方が安全性が高そう
  • stageだけ先にAuroraに移行して検証する
    • その際に移行にかかる時間を計測しておくと、ダウンタイムが予測しやすくなる
  • 失敗時の切り戻しプランを用意する
  • 書き込みの切替時には、旧masterからの書き込みトラフィックを遮断する
    • セキュリティグループで対象のボートを閉じるのが簡単そう
  • カスタムエンドポイントで、バッチや検証用DBなど高負荷のreadが発生するトラフィックと、webサービスが使用するリードレプリカを分ける
  • 旧DBとAuroraのタイムゾーンをあわせる
  • Auroraのフェイルオーバー時のコネクションプーリング対策

その他

  • 現在は、Aurora(MySQL5.7)があり、Performance Insightsもサポートされている

各社の移行事例

BASEのメインDBをAurora(MySQL)に移行しました - BASE開発チームブログ

  • 一番最新っぽい事例

記事執筆時期

2018/11月

移行内容

  • RDS(MySQL5.6) -> Aurora(MySQL5.6)

移行理由

  • 運用負荷の削減
    • MySQL on EC2の方が多機能だがスキルのあるエンジニアを貼り付ける必要がある
    • MySQLの新機能と、RDSのスループットのトレードオフ
    • ベンダーロックインというリスク
  • 速度
    • レプリカラグが発生しない
    • クエリキャッシュが有用
    • スループットの向上
  • 費用(分析、集計用DBをリードレプリカに移行できたため
  • その他Auroraの機能
    • カスタムエンドポイント(分析用レプリカを運用していた
    • 無停止パッチ(運用コスト削減
    • Performance Insights
    • 監査ログ
    • 高速クローン

移行方法

  • 既存環境からAuroraのMasterにレプリケーション
    • テンポラリの書き込み可能MySQLレプリカを間に挟んでいる

サービスに影響しない書き込み可能レプリカを入れることで、「mysql.rds_stop_replication」と「mysql.rds_start_replication」コマンドにてレプリケーションを操作し、レプリケーションを止めている間にテンポラリに対して巨大テーブルへのALTER文等を実施して事前にAuroraのテーブル定義をカスタマイズできます。弊社で言えば大きめのテーブルへのインデックス追加はこのやり方で実施しておきました。row_formatをDynamicに書き換えておきたい場合などはこの方法で実施すれば夜間メンテなどで長い時間サービスを停止する必要なく実施できます。

  • CNAME内のエンドポイント情報を全てAurora側に向けることで移行

移行結果

  • リードレプリカのタイムラグが減った

特記事項

  • Aurora Serverlessの検討
    • インスタンス用パラメータグループにあるinnodb_file_formatでbarracuda指定ができないので見送り
    • barracudaはファイルを圧縮して、IOスループットを上げるオプション
  • MySQLのバージョン
  • テーブル圧縮はコマンドが通るけど実際には圧縮されないので気付きにくいらしい

MySQLからPostgreSQLへの移行とDBリファクタリング/postgresqlJapan2018 - Speaker Deck

記事執筆時期

2018/11月

移行内容

Aurora(MySQL) -> Aurora(PostgreSQL)

移行理由

RDBのリファクタリング

  • PostgreSQLへの移行理由
    • トリガーを柔軟に定義できる
    • トリガーに指定するユーザー定義関数の言語が複数選択できる

移行方法

  • Amazon DMSを使用
    • レプリケーションのタイミングでDBトリガーを発動し、リファクタリングしたテーブルに合わせた内容にデータを加工している
  • APIサービスを新たに立ててDBへのアクセスをAPIにwrap
    • API内で、徐々に新DBに移行

特記事項

  • 移行失敗の監視
    • Mackerelのチェックプラグインを使用
    • エラーログを監視して何かあったら通知
    • CloudWatchでできそうな...?

RDS for MySQLからAuroraへの移行 〜Auroraリードレプリカを利用した低コスト移行方式〜 - コネヒト開発者ブログ

記事執筆時期

2018/8月

移行内容

  • RDS(MySQL5.6) -> Aurora(MySQL5.6)

移行理由

  • 可用性を担保しながら、コストメリットが享受できる
  • 運用面のメリットを享受したかった

移行方法

Aurora リードレプリカ -> masterへ昇格

  • Auroraリードレプリカを本番の読み取りトラフィックにカナリアリリース
  • ステージング環境を、Aurora構成に切替
  • 本番の読み取りトラフィックを100%Aurora化
  • サービス用途以外のレプリカDBを、Auroraをマスターとするbinlogレプリにて作成
  • 書込トラフィックのAuroraへの切替
    • 書き込みトラフィックを遮断

      切替時の書込データロストを防ぐ為の書込トラフィック遮断 RDSマスタをリードオンリーモードにするのが正攻法なところですが、SecurityGroupを使い、L4レイヤでMySQLへの通信を遮断する方法を選択しました。

    • 最終書込のAuroraへのレプリケーション確認
    • Auroraマスター昇格
    • 書込トラフィックをAuroraへ切替

特記事項

  • 書き込み切替のダウンタイムは3分強
  • 「複数のリーダエンドポイントを設けたい」と書かれているが、現在はカスタムエンドポイントで対応可能

Aurora リードレプリカを使用した PostgreSQL から Aurora への移行 - TerraSkyBase

記事執筆時期

2018/8月

移行内容

RDS(PostgreSQL) -> Aurora(PostgreSQL)

移行理由

なし。検証

移行方法

Aurora リードレプリカ -> masterへ昇格

  • Aurora リードレプリカの作成
    • RDSのリードレプリカとして設定する
  • 同期完了後に昇格
    • ドキュメントでは Aurora リードレプリカのレプリカラグを"0" にするとあるが、実質不可能なので、トランザクションを停止させてから昇格した
  • DB クラスターロールが、「レプリカ」から「マスターになる」

雑感

EC2上のMySQLからAuroraへの移行作業で失敗・想定外だったこと - シンクロ・フード エンジニアブログ

記事執筆時期

2018/5月

移行内容

EC2上の MySQL -> Aurora(MySQL)

移行理由

不明

移行方法

自前でAuroraのmasterにレプリケーションを貼る。同期完了後、切替

特記事項

  • Aurora→外側のMySQLサーバという向きではレプリケーションがSSL通信できない
    • 別リージョンのMySQLサーバにバックアップを取っていた
    • 結局クロスリージョンレプリカを使用
  • 移行前のMySQLサーバと、移行予定のAuroraサーバのTimezoneがズレていることにより、移行当日のデータ差分チェックで差分が発生
  • フェイルオーバー後、クライアント側のコネクションプーリングが、新リードレプリカをmasterと誤認する
    • rails側でコネクションプーリングを定期的にリセットするように設定したらしい

RDS for MySQL5.7 から Aurora(MySQL 5.6 互換)へ移行しました - Feedforce Developer Blog

  • へいしゃblog

記事執筆時期

2017/10月

移行内容

RDS(MySQL5.7) -> Aurora(MySQL5.6)

  • 当時はMySQL5.7未サポート

移行理由

ストレージの自動スケーリング 、耐障害性 という主に運用目線で見たときの強みを評価し、採用しました。

移行方法

既存 DB を RDS へ最小限のダウンタイムで移行することができる AWS Database Migration Service (以下 DMS) を利用して、データ移行を行いました。

  • Auroraを参照する環境を新しく作成して、BuleGreenDeploymentで移行

特記事項

  • MySQL5.6 -> 5.7へのダウングレード
    • 影響範囲の調査が必要だった
    • ROW_FORMAT が変わるので、migrationを用意して、db:migrate した
    • mysql-devel のバージョンを 5.7 から 5.6 にダウンさせる必要があった
  • DMS
    • Terraformでコード化しなかったけど、コード化した方が安全だった
    • null 制約 や AUTO_INCREMENT 属性といった情報は移行されないので、mysqldump でテーブル定義だけ先にdumpした
    • 外部キーが設定されたテーブルのデータ移行が失敗することがあるので、追加の接続属性 に initstmt=SET FOREIGN_KEY_CHECKS=0 を追加
  • 所要時間の把握が不足していた。事前にリハーサルを行って把握しておくべき

【ヒカラボ】RDS for MySQL → Aurora

記事執筆時期

2016年1月

  • AmazonDMSリリース前

移行内容

RDS(MySQL5.6) -> Aurora

移行理由

  • パフォーマンスの向上
  • メンテナンスの削減
  • 負荷対策(リードレプリカ15台)
  • コスト削減

移行方法

RDSのリードレプリカからスナップショットを作成後、RDS->Auroraにレプリケーションを貼る

  • その後、参照系、更新系と、Auroraに移行
  • 移行後、切り戻しができるように、逆方向にレプリケーションを貼り直した

移行結果

  • パフォーマンスは目立つほど向上していない
  • インスタンス作成時間が短縮された

特記事項

  • writerインスタンスを再起動すると、フェイルオーバーが発生することがある
  • 昇格を前提にした設計に変更した
    • インスタンス名を昇格を前提にした名前に変更した
      • db-master db-slave01.. を instance001, instance002... )
    • readerとwriterのセキュリティグループを統一

その他の移行事例

Amazon Aurora を知らなかった人が 2018/12 現在の Aurora の最新状況を分かる範囲でまとめた

主にMySQLについて調査

Amazon Aurora って何?

MySQL/PostgreSQL 互換のリレーショナル・データベース

サポートしているエンジンは?

  • MySQL 5.7
  • MySQL 5.6
  • PostgreSQL 9.6

RDSと何が違うの?

  • 高パフォーマンス
  • スケーラブル
  • 低運用コスト(人もお金も)
  • 耐障害性

高パフォーマンス

標準的なMySQLより5倍、PostgreSQLより3倍早い

  • ストレージノードでのクエリの並列化
  • レプリケーション時にslaveに書き込み負荷がかからない
    • ストレージは個別に更新されて、slaveがやるのはログの位置情報の更新とキャッシュクリアだけ
  • AWSに最適化されたチューニング

スケーラブル

低運用コスト

DBの専門家でなくても運用しやすい

  • 各種パラメータはすでに最適化済
  • Performance Insights で、ボトルネックや問題を専門家でなくても発見可能に
  • 自動フェイルオーバー
  • zero-downtime-patching(ZDP)
    • データベースエンジンの更新時にダウンタイムが発生しない(すごい!)

耐障害性

  • インスタンスの自動修復
  • 3つのAZにそれぞれ2つのリードレプリカが作られる

その他便利な機能

  • backtrack : DBのデータを巻き戻すことが可能
  • database clone: データの複製が一瞬で行われる(参照元はwriteされるまで同じ
  • IAM権限でログイン可能(高負荷アプリケーションの接続には向かないらしい
  • lambdaを実行可能(!)
    • mysql.lambda_async というストアドプロシージャからlambdaを呼べる
  • S3への自動バックアップ
  • Performance Insights
    • 専門家でなくてもクエリパフォーマンスの評価やDBの状態を把握可能に
  • Parallel Query
    • 1つのSQLを複数のストレージノードとCPUで並列実行して高速化する
    • いろいろ制限もある
  • Aurora Serverless
    • インスタンスタイプの指定が不要 & オートスケール
    • アクセスが一定時間発生しないと休眠する
    • 開発環境や、低頻度アクセスのDB、社内システムなど向け?
    • 機能に制限がある 👉 see: Amazon Aurora サーバーレスの使用 - Amazon Aurora

知っておくべき用語

  • パラメータグループ
  • エンドポイント

パラメータグループ

Auroraでは、エンジン(MySQLとか)のパラメータを パラメータグループ という単位で設定する

パラメータグループは、 DB クラスターパラメータDB インスタンスパラメータ に分かれる

  • DB クラスターパラメータ : クラスタ全体に適用されるパラメータ
  • DB インスタンスパラメータ : インスタンス毎に設定されるパラメータ

Auroraの構成に最適化されているので、基本的にはいじる必要は無い

参考

エンドポイント

4つのエンドポイントがある

  • クラスタエンドポイント : masterノードのエンドポイント read/writeが可能
  • 読み取りエンドポイント : リードレプリカへのエンドポイント readのみ。適当に負荷分散してくれる
  • インスタンスエンドポイント : 個別の各DBサーバへのエンドポイント
  • カスタムエンドポイント : インスタンスをユーザが組み合わせてエンドポイントを作成できる
    • たとえば、バッチ処理など重たい処理専用のリードレプリカを決めて、webサービスではそれ以外のリードレプリカを使うとか

参考

Auroraに最適化するためにやること

  • 1つのトランザクションで大量のread/writeをする場合、SQLを分けると高速化されやすいとのこと
    • 並列実行に特化したチューニングをしているらしい
    • Parallel Queryを使う場合はあんまり考える必要がなくなるのかな?
  • マシン性能に比例してパフォーマンスが上がるようにチューニングされているので、遅いときはマシンの性能を上げるのが手っ取り早いとのこと

制約事項

  • ストレージの最大容量が64TB
  • リードレプリカは最大15台
  • MySQLでは、InnoDB/ROW_FORMAT=Dynamicのみサポート
    • ROW_FORMAT= COMPRESSED が使えない(データを圧縮することで、IOのコストを下げるオプション)
  • Performance Insightsは、T2インスタンスで走らせるとメモリが不足する

あんまりわかっていないこと

  • zero-downtime-patchingでは、Auroraのバージョンアップはできるけど、エンジン(MySQLとか)の更新はできない?
  • リードレプリカのスケジュールに基づいたスケーリングってできるのかな?
    • 例えば週末だけリードレプリカを予め増やしておきたいとか
  • Auroraへの移行ってどうするの?
  • 全文検索エンジンは入れられる?

参考リンク

javascript の generator おぼえがき

ECMAScript 6 にて実装された

generator って?

generatorそのものの解説はこちらが詳しい

Pythonのジェネレーターってなんのためにあるのかをなるべく分かりやすく説明しようと試みた - Qiita

  • 要するに、ステートを持ち、呼ばれるたびにそのステートを使った処理結果を返すオブジェクト
  • 超でかいファイルを1行づつ読み込む時とか、必要な時に必要なだけ実行される処理が欲しい時に使う
  • rubyだと Enumerator::Lazy とかが近い?

実装方法

generator関数

  • generatorを生成する関数
  • generator関数は function* で宣言できる
  • generator関数の中では、yield を設定できる(後述)

generator

  • generator関数 の戻り値として生成される
  • next() を実行すると、generator関数で設定した yield のところまで実行される
  • next() の戻り値は、 valuedone のキーを持つ Hash
    • value : yield の引数
    • done
      • なにか処理が実行されていれば true
      • 何も処理が実行されていれば false

サンプル

function* generator(){
  yield 1;
  yield 2;
  yield 3;
}

g = generator();

console.log(g.next());
console.log(g.next());
console.log(g.next());
console.log(g.next());
console.log(g.next());
{ value: 1, done: false }
{ value: 2, done: false }
{ value: 3, done: false }
{ value: undefined, done: true }
{ value: undefined, done: true }

サンプル2

遅延評価的なことをやってみる

function* fibonacci_generator(){
  var a = 0;
  var b = 1;

  while(true) {
    const c = b
    b = a + b;
    a = c;
    yield b
  }
}

const fibonacci = fibonacci_generator();

for(var i=0; i<10; i++){
  console.log(fibonacci.next());
}
{ value: 1, done: false }
{ value: 2, done: false }
{ value: 3, done: false }
{ value: 5, done: false }
{ value: 8, done: false }
{ value: 13, done: false }
{ value: 21, done: false }
{ value: 34, done: false }
{ value: 55, done: false }
{ value: 89, done: false }

こっちは終わらない処理なので、 done はずっと false

ディスク容量が足りなすぎて growpart でエラーが出た時にやったこと

概要

  • あまりにもディスク容量が無いと、growpart に失敗する
  • その時にディスク容量を減らすために、何を削除したのかの記録

growpartのエラー

$ sudo growpart /dev/nvme0n1 1
mkdir: cannot create directory '/tmp/growpart.7531': No space left on device
FAILED: failed to make temp dir

ディスク容量削減のためにやったこと

apt-get autoremove は不要なパッケージを削除してくれるコマンドで、ディスク容量の削減にもってこいだが、あまりにもディスク容量が少ないとエラーが発生する

$ sudo apt autoremove
Reading package lists... Error!
E: Write error - write (28: No space left on device)
E: IO Error saving source cache
E: The package lists or status file could not be parsed or opened.

その場合、以下のように不要ファイルを地道に削除していく必要がある

不要ファイルの削除

以下のファイルを削除して容量を確保した

  • /var/cache/apt/archives/*deb
  • /var/cache/apt/pkgcache.bin.*
  • /var/log/*/.gz

/var/cache/apt/archives/*deb

apt-get でダウンロードしたpackageのcache

/var/cache/apt/pkgcache.bin.*

apt-get autoremove (後述) がディスク容量不足で実行失敗した時に発生したゴミファイル

/var/log/*/.gz

過去のログ。もう見なさそうなので削除

EC2にてubuntuのEBSの容量が不足した時にやったこと

概要

  • AWS EC2 でストレージ(EBS)の容量が不足した
  • EBSの管理画面から設定変更すればすぐ変更可能かと思ったらやることがまだあったので、忘れない内にまとめる

OS

Ubuntu 16.04.2 LTS (GNU/Linux 4.15.0-36-generic x86_64)

やったこと

  • EBSの管理画面からストレージの容量を変更
  • growpart コマンドで、拡張したストレージをパーティションに追加
  • resize2fs コマンドで、ファイルシステムに拡張されたパーティションを認識させる

EBSの管理画面からストレージの容量を変更

EBSを選択 → 「アクション」 → 「ボリュームの変更」

f:id:kasei_san:20181119160055p:plain

説明のところに編集ボタンがあると思ってしばらく探した...

この時点では、ストレージこそ16GBになったが、 追加された8GBはどのパーティションにも属していない状況

$ lsblk
NAME        MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
nvme0n1     259:0    0  16G  0 disk 
└─nvme0n1p1 259:1    0   8G  0 part /
  • lsblk はブロックデバイスの情報を取得するコマンド

growpart コマンドで、拡張したストレージをパーティションに追加

$ sudo growpart /dev/nvme0n1 1
CHANGED: partition=1 start=2048 old: size=16775135 end=16777183 new: size=33552351,end=33554399

無事拡張された

$ lsblk
NAME        MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
nvme0n1     259:0    0  16G  0 disk
└─nvme0n1p1 259:1    0  16G  0 part /

growpart は、ストレージの空き領域をパーティションに追加するコマンド

resize2fs コマンドで、ファイルシステムに拡張されたパーティションを認識させる

拡張前

$ df
Filesystem     1K-blocks    Used Available Use% Mounted on
/dev/nvme0n1p1   8065444 3562788   4486272  45% /
$ sudo resize2fs /dev/nvme0n1p1
resize2fs 1.42.13 (17-May-2015)
Filesystem at /dev/nvme0n1p1 is mounted on /; on-line resizing required
old_desc_blocks = 1, new_desc_blocks = 1
The filesystem on /dev/nvme0n1p1 is now 4194043 (4k) blocks long.

拡張後

$ df
Filesystem     1K-blocks    Used Available Use% Mounted on
/dev/nvme0n1p1  16197524 3563812  12617328  23% /

resize2fs は、パーティションの変更やマウントなどのデバイス上の変更をファイルシステムに反映するコマンド(っぽい

javascript の Promise おぼえがき

AWS lambda で Node.js と向きう合う必要が出てきたので、最近のモダンな JS を理解する

最近は async/await が使えるらしいが、内部的には Promise を使っているらしいので、まずは Promise から理解する

Promise とは?

非同期処理の実行結果(成功 or 失敗) の受け取り口を用意して、非同期の管理を容易にするためのオブジェクト

コールバック地獄のしんどさを回避するために生まれた

つかいかた

new Promise する

  • 第一引数は、引数が2つある関数
    • 関数の第1引数には、正常終了時に呼び出す関数
    • 関数の第2引数には、異常終了時に呼び出す関数
  • new Promise の戻り値は、then()catch() を持つオブジェクト
    • then() には正常終了時に呼ばれる関数を渡す
    • catch() には異常終了時に呼ばれる関数を渡す
      - 例外を勝手に拾ってくれたりはしないので、自前で例外をコントロールする必要がある
      
  • then()catch() が呼ばれた時点で、promise に渡した関数が実行される

正常終了例

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('foo');
  }, 10);
});

promise.then((value) => {
  console.log("then");
  console.log(value);
}).catch((value) => {
  console.log("catch");
  console.log(value);
});

出力結果

then
foo

異常終了例

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('foo');
  }, 10);
});

promise.then((value) => {
  console.log("then");
  console.log(value);
}).catch((value) => {
  console.log("catch");
  console.log(value);
});

出力結果

catch
foo

Promise のチェーン

then() の戻り値を Promise オブジェクトにすることで、Promise をチェーンできる

これにより、非同期処理を同期的に実行できる

const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('promise1');
  }, 10);
});

promise1.then((value) => {
  console.log("then 1");

  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('promise2');
    }, 10);
  });

}).then((value) => {
  console.log("then 2");
});

出力結果

then 1
then 2

Promise#all

複数 Promise の並列実行ができる

正常終了例

const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log('promise1')
    resolve('promise1');
  }, 10);
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log('promise2')
    resolve('promise2');
  }, 10);
});

Promise.all([
  promise1, promise2
]).then((value) => {
  console.log(value)
  console.log('finished')
});

then() には、各 Promiseresolve の引数を配列にしたものが渡される

promise1
promise2
[ 'promise1', 'promise2' ]
finished

異常終了例

1つでも reject() が呼ばれたら catch() が呼ばれる

const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('promise1');
  }, 10);
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('promise2');
  }, 10);
});

Promise.all([
  promise1, promise2
]).then((value) => {
  console.log(value)
  console.log('finished')
}).catch((value) => {
  console.log(value)
  console.log('catch')
});

出力結果

promise2
catch

reject() が複数発生する場合も、早いもの勝ちで最初の reject() の引数が渡される 処理自体は最後まで行われる

Promise.race()

複数 Promise のうち、1つでも処理が完了したら then() が呼ばれる