Edited at

約束って面倒くさい。

More than 3 years have passed since last update.

Promiseを使えばコールバック地獄から解放され、コードの可読性が向上する!!

そう思っていた時期が私にもありました。

コールバック地獄からは解放されたものの可読性についてはいまだに疑問です…

試行錯誤しながら使っているものの、忘れっぽいのでメモっておきます。

Node v4.2.6で検証


メソッドチェーン

jQueryでもおなじみ、みんな大好きメソッドチェーン。

thenはPromiseオブジェクトを返してくれるのでメソッドチェーンができます。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise/then


まずは基本形

'use strict';

Promise.resolve("hogehoge")
.then(function(result){
console.log("then1", result); // <- result="hogehoge"
return Promise.resolve(result); // 成功のPromiseオブジェクトを返す
}).then(function(result){
console.log("then2", result); // <- result="hogehoge"
return Promise.resolve("fugafuga"); // 成功のPromiseオブジェクトを返す
}).then(function(result){
console.log("then3", result); // <- result="fugafuga"
// Promiseオブジェクトを返さない
}).then(function(result){
console.log("then4", result); // <- result=undefined
// Promiseオブジェクトを返さない
}).then(function(){
console.log("then5");
});


result

then1 hogehoge

then2 hogehoge
then3 fugafuga
then4 undefined
then5

エラーがないと上から順番に実行されてくのでわかりやすいですね。


そもそもPromiseオブジェクトを返さなくても動く

(; ・`д・´) ナ、ナンダッテー!!

なんか気持ち悪い。。。

'use strict';

var promise_chain = Promise.resolve()
.then(function(){
return true;
}).then(function(result){
console.log("then-bool1", result);
return false;
}).then(function(result){
console.log("then-bool2", result);
return 1;
}).then(function(result){
console.log("then-int1", result);
return 0;
}).then(function(result){
console.log("then-int2", result);
return "fugafuga";
}).then(function(result){
console.log("then-string1", result);
return "";
}).then(function(result){
console.log("then-string2", result);
return [];
}).then(function(result){
console.log("then-array", result);
return {};
}).then(function(result){
console.log("then-object", result);
return null;
}).then(function(result){
console.log("then-null", result);
});

console.log("start!");
promise_chain.then(function(){
console.log("end!");
}).catch(function(err){
console.log("catch", err);
});

start!

then-bool1 true
then-bool2 false
then-int1 1
then-int2 0
then-string1 fugafuga
then-string2
then-array []
then-object {}
then-null null
end!


エラー処理


どこでコケてもcatchが捕捉してくれる安心感

(*>△<)<Promiseチェーンの途中でエラーが起きたらどうしよう!


基本形

'use strict';

Promise.reject("エラーだよ~(o・∇・o)")
.then(function(result){
console.log("then", result); // <- 呼ばれない
})
.catch(function(err){
// 失敗のPromiseオブジェクトをcatchする
console.log("catch", err);
});


result

catch エラーだよ~(o・∇・o)


そんな心配はわりと不要でした。意外とサクッと書けるんだなー。


チェーンの途中でこけても大丈夫!

Promise使うならエラーもPromiseで返した方がいいかもしれませんね。

'use strict';

Promise.resolve("hogehoge")
.then(function(result){
console.log("then1", result); // <- 呼ばれる
return Promise.reject("エラーだよ~(o・∇・o)");
})
.then(function(result){
console.log("then2", result); // <- 呼ばれない
})
.catch(function(err){
// 失敗のPromiseオブジェクトをcatchする
console.log("catch", err);
});


result

then1 hogehoge

catch エラーだよ~(o・∇・o)


例外まで受け取れる優れモノ!

これが一番面白い動きかも。

しかしこの仕様を知らなかったせいで、どハマりすることに…(しろめ)

'use strict';

Promise.resolve("hogehoge")
.then(function(result){
console.log("then1-1", result); // <- 呼ばれる
throw new Error("(*>△<)<ナーンナーンっっ");
console.log("then1-2", result); // <- 当然呼ばれない
return Promise.resolve("fugafuga");
})
.then(function(result){
console.log("then2", result); // <- 呼ばれない
})
.catch(function(err){
// throwもcatchする!
console.log("catch", err);
});


result

then1-1 hogehoge

catch [Error: (*>△<)<ナーンナーンっっ]


例外の罠

PromiseでくるまれるとPromiseのcatchですべて受けてしまうので注意。

これのせいで1時間ほどハマりしました。。


ダメな例


promise_exception_ng.js

'use strict';

function p_exception() {
return (new Promise(function(resolve, reject){
var hoge = null;
resolve(hoge.fuga); // <- 例外発生
}));
}

try {
p_exception().then(function(result){
console.log("then", result);
});
} catch(e) {
console.log("catch", e); // <- ここにこない!
}



result

$ node promise_exception_ng.js

$ echo $?
0


OKな例

何が起こるかわからないのでPromiseでくるんだらとりあえずcatchは書いておいたほうがよさそう。

'use strict';

function p_exception() {
return (new Promise(function(resolve, reject){
var hoge = null;
resolve(hoge.fuga); // <- 例外発生
}));
}

p_exception()
.then(function(result){
console.log("then", result);
})
.catch(function(err){
console.log("catch", err);
});


result

catch [TypeError: Cannot read property 'fuga' of null]



だがしかし…

当然っちゃ当然ですが、Promiseでくるんでない部分の例外は従来のtry~catchで処理しなければならない。

(*>△<)<面倒くさいー!

'use strict';

function n_exception() {
var hoge = null;
var piyo = hoge.fuga; // <- 例外発生
return (new Promise(function(resolve, reject){
resolve(piyo);
}));
}

try {
n_exception()
.then(function(result){
console.log("then", result);
})
.catch(function(err){
console.log("catch", err); // <- ここにこない!
});
} catch(e) {
console.log("catch2", e); // <- ここで無事catchされる
}

catch2 [TypeError: Cannot read property 'fuga' of null]

終わりだよ~(o・∇・o)