SlideShare a Scribd company logo
Promiseでコールバック地獄から解放された話
杉浦颯太(sugiuras)
はじめまして
• 杉浦 颯太(すぎうら そうた)と申します
• 2015年入社予定のクソザコ大学生です
• 現在はアルバイトとして働かせてもらっています
• 音楽好きです
• Twitter / GitHub: @sota1235
バイト初めて一ヶ月…
• 14入社の方とかに「来年上司になる方ですカァ!!!」って られる
• みんなすぐ僕より(物理的に)低い位置でお話しようとする
• あんまりいじめないでください
アジェンダ
1. コールバック地獄の話
2. Promiseの話
3. 実装例
アジェンダ
1. コールバック地獄の話
2. Promiseの話
3. 実装例
例えば
• こんなコードが書きたい
• 「hoge APIを叩いて∼」
• 「その結果を元にリクエスト作ってmoge APIを叩いて∼」
• 「さらにその結果をもとにfuga APIを叩いて∼」
• 「それをページに表示させたいヾ(ó `o)ノ」
書いてみた\(^0^)/
こうなる/(^0^)\
$.get("http://api.hoge.com?hoge=hoge", function(err, hoge) {
if(err) {
console.error(err);
return;
}
$.get(“http://api.moge.com?moge="+hoge, function(err, moge) {
if(err) {
console.error(err);
return;
}
$.get(“http://api.fuga.com?fuga="+moge, function(err, fuga) {
if(err) {
console.error(err);
return;
}
$('body').val(fuga);
});
});
});
これぞコールバック地獄
• きれいな書き方もあるかもしれないが、安直に書けばこんな感じ
• 「さらにこのAPIも噛ませて…」とかなるとどんどんネストが…
• 「あ、この処理をパラメータ変えて3回飛ばして」とか上司に言
われたらもう…
• 読むのも書くのもつらいですよね
そこでPromise
Promiseとはなんぞやと、この地獄の抜け出し方を見てみましょう
アジェンダ
1. コールバック地獄の話
2. Promiseの話
3. 実装例
Promise is 何
• 非同期処理の結果を抱え込むオブジェクト
• 処理の結果に影響されることなく値を返す約束手形を渡してくれる
のがPromiseオブジェクト
• この約束手形をつないだりすることでネストが深くなるのが防げ
る
• また、処理の順序等の制御が簡単になる
なるほどわからん(́・ω・`)
インスタンス生成
• new Promise(); ←こんだけ!
• 基本的な使い方は
1. 関数を作る
2. Promiseインスタンスを生成する
3. インスタンスの引数に処理を書き、約束を取り付ける
Promise化された関数
/* not Promise */
var func = function(num, callback) {
if(num % 2 == 0) {
callback(null, num / 2);
} else {
callback("Error", null);
}
}
!
/* Promise */
var pfunc = function(num) {
var promise = new Promise(function(resolve, reject) {
if(num % 2 == 0) {
resolve(num / 2); // success
} else {
reject(“Error"); // failure
}
});
return promise;
}
Promise化された関数を使う
/* not Promise */
func(4, function(err, result) {
if(err) {
console.error(err);
return;
}
console.log(result);
});
!
/* Promise */
pfunc.then(function(result) {
console.log(result); // success
}).catch(function(err) {
console.error(err); // failure
});
.then()
• resolve()された結果を受け取るメソッド
! var p = new Promise(function(resolve, reject) { resolve("Hello, World!"); });
!
p.then(function(result) {
console.log(result); // Hello, World!
});
.catch()
• reject()された結果を受け取るメソッド
• いわゆるエラー処理
!
!
var p = new Promise(function(resolve, reject) { reject(“ERROR!!”); });
!
p.catch(function(err) {
console.error(err); // ERROR!!
});
.all()
• Promiseオブジェクトが持つメソッド
• 引数にPromiseインスタンスの配列を渡す
• 全てのインスタンスの処理が終わった時、配列の順番に結果が格納
された配列が帰ってくる
!
!
.all()
var p1 = new Promise(function(resolve, reject) { resolve(“1”); });
var p2 = new Promise(function(resolve, reject) { resolve(“2”); });
var promises = [p1, p2];
Promise.all(promises).then(function(results) {
console.log(results); // [1, 2]
});
.race()
• Promiseオブジェクトが持つメソッド
• 引数にPromiseインスタンスの配列を渡す
• 全てのインスタンスのどれか1つでも処理が終わるとその結果だけ
を返す
• 1つの処理が終わった時点で他の処理が破棄されるわけではないの
で注意
!
var p1 = new Promise(function(resolve, reject) { resolve("1"); });
var p2 = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve("2");
}, 1000);
});
var promises = [p1, p2];
Promise.race(promises).then(function(result) {
console.log(result); // 1
});
.race()
結局Promiseの何が嬉しいの
• ネストが浅くなる
• 非同期処理の制御が容易になる
• メソッドチェーンで読みやすく
• 非同期関数のエラー処理の仕様が強制的に統一される
• reject()でエラー処理を行うため
• 全てはcatch()に吸収される
注意すべきとこ
• テストをするのに工夫が必要(らしい)
• サポートしてないブラウザがある
• → polyfillライブラリを使用することで解決できる
• es6-promiseというのが一番よさげ
アジェンダ
1. コールバック地獄の話
2. Promiseの話
3. 実装例
さっきのコールバック地獄
$.get("http://api.hoge.com?hoge=hoge", function(err, hoge) {
if(err) {
console.error(err);
return;
}
$.get(“http://api.moge.com?moge="+hoge, function(err, moge) {
if(err) {
console.error(err);
return;
}
$.get(“http://api.fuga.com?fuga="+moge, function(err, fuga) {
if(err) {
console.error(err);
return;
}
$('body').val(fuga);
});
});
});
関数化 & Promise化
• 先ほどの3つのAPIを叩く処理を別々に関数化する
• かつPromise化する
関数化 & Promise化(hoge API)
var hoge = function() {
return new Promise(function(resolve, reject) {
$.get("http://api.hoge.com?hoge=hoge", function(err, hoge) {
if(err) reject(err);
resolve(hoge);
});
});
}
関数化 & Promise化(moge/fuga API)
var moge = function(hoge) {
return new Promise(function(resolve, reject) {
$.get("http://api.moge.com?moge"+hoge, function(err, moge) {
if(err) reject(err);
resolve(moge);
});
});
}
var fuga = function(moge) {
return new Promise(function(resolve, reject) {
$.get("http://api.fuga.com?fuga"+moge, function(err, fuga) {
if(err) reject(err);
resolve(fuga);
});
});
}
つなげる!!!!
hoge.then(moge)
.then(fuga)
.then(function(result) {
$('body').val(result);
})
.catch(function(err) {
console.error(err);
});
つなげる!(補足)
hoge
.then(moge)
/*
* hoge.then(function(result) { moge(result) }) と同じ
*/
浅くなったー(*́ω`*)
• then()とcatch()をつなげて処理を渡していくのでネストが深くな
らない
• Promiseオブジェクト以外の処理も渡すことができる
• Promiseでなくても明示的にreturnすることでまたthen()をつな
げることができる
参考
• Promiseの本
• http://azu.github.io/promises-book/
ご静聴ありがとうございました

More Related Content

Promiseでコールバック地獄から解放された話