Node.js
promise
nodejs
メソッドチェーン

Promiseのメソッドチェーンの使い方まとめ

nodeのPromiseを使って、同期的な処理を書けるようになったので、その備忘録

やりたいこと

nodeのPromiseを使って、同期的な処理を書く

非同期の例

test.txtを読み込んで、test2.txtを読み込むという例です。
test.txtを読み込むこととtest2.txtを読み込むのをログで監視しています。

var fs = require('fs');
console.log('処理1 start!!!');
fs.readfile('test.txt','utf8',function(err,data){
       console.log('fs1 end!!!');
});
console.log('処理1 end!!!')

console.log('処理2 start!!!');
fs.readfile('test2.txt','utf8',function(err,data){
       console.log('fs2 end!!!');
});
console.log('処理2 end!!!')

}

nodeを実行するとこんな感じでログに吐き出されます。

処理1 start!!!
処理1 end!!!
処理2 start!!!
処理2 end!!!
fs1 end!!!
fs2 end!!!

fs.readfile関数は非同期関数のため、fs.readfileの処理が終了する前に次の処理を行い、fs.readfileの処理が終わったら内部処理を行うため、fs1 end!!!が後からログに出力されます。
以下のように出力するようにPromiseを使用します。

処理1 start!!!
fs1 end!!!
処理1 end!!!
処理2 start!!!
fs2 end!!!
処理2 end!!!

基本的な構文

function func1(){
    return new Promise(function(resolve, reject){
       resolve();
    });
}

function func2(){
    return new Promise(function(resolve, reject){
       resolve();
    });
}

function onRejected(error) {
    console.log("error = " + error+"\r\n");
    process.exit(1);
}

var promise = Promise.resolve();
promise
    .then(func1)
    .then(func2)
    .catch(onRejected);

解説

実際にコードが動くのはvar promiseのところからです。
funcAのresolveが処理されてからfuncBが処理されます。処理の中でエラーがあった際はonRejectedが走ります。

非同期処理を同期的に書く例

上で書いた非同期処理を同期的に処理します。

var fs = require('fs');
function func1(){
    return new Promise(function(resolve, reject){
       console.log('処理1 start!!!');
       fs.readfile('test.txt','utf8',function(err,data){
           console.log('fs1 end!!!');
           console.log('処理1 end!!');
           resolve();
       });
    });
}

function func2(){
    return new Promise(function(resolve, reject){
       console.log('処理2 start!!!');
       fs.readfile('test.txt','utf8',function(err,data){
           console.log('fs2 end!!!');
           console.log('処理2 end!!');
           resolve();
       });
    });
}

function onRejected(error) {
    console.log("error = " + error+"\r\n");
    process.exit(1);
}

var promise = Promise.resolve();
promise
    .then(func1)
    .then(func2)
    .catch(onRejected);

解説

まずthen(func1)でfunc1の処理が実行されます。
まず3行目、4行目が走ります。fs.readfileは非同期関数のため処理が終了するまで5~8行目は実行されません。
またresolveは7行目あるため、func1の処理は終了せずfunc2には行きません。
fs.readfileの処理が終了したら5→6→7と処理が実行され、めでたくfunc2に行きます。

1  function func1(){
2      return new Promise(function(resolve, reject){
3         console.log('処理1 start!!!');
4         fs.readfile('test.txt','utf8',function(err,data){
5             console.log('fs1 end!!!');
6             console.log('処理1 end!!');
7             resolve();
8         });
9      });
10 }

結果として以下のように出力されます。

処理1 start!!!
fs1 end!!!
処理1 end!!!
処理2 start!!!
fs2 end!!!
処理2 end!!!

エラー処理(reject)

例えば上記の処理でtest1.txtが存在しないとします。
その場合4行目のfs.readfileでonRejectedに入り、process.exit(1)で処理が終了します。
onRejectedの中のprocess.exit(1)はこれを書かないと元の処理のエラーがあった行の次の行から実行されるため、強制終了しています。

resolveの使い方

resolveはPromiseオブジェクトを返します。
型は文字列でも配列でもなんでも良いです。
下記の例ではString型を返しています。

var fs = require('fs');
function func1(){
    return new Promise(function(resolve, reject){
       console.log('処理1 start!!!');
       fs.readfile('test.txt','utf8',function(err,data){
           console.log('fs1 end!!!');
           console.log('処理1 end!!');
           resolve(data);
       });
    });
}

function func2(value){ // 引数に前処理でresolveした結果が渡される
    return new Promise(function(resolve, reject){
       console.log('処理2 start!!!');
       console.log(value); // <--追加
       fs.readfile('test.txt','utf8',function(err,data){
           console.log('fs2 end!!!');
           console.log('処理2 end!!');
           resolve();
       });
    });
}

function onRejected(error) {
    console.log("error = " + error+"\r\n");
    process.exit(1);
}

上記func1でresolveでfs.readfileの中身「data」を返しています。
次の処理であるfunc2に引数を指定すると、それに前処理のresolveの結果が渡されます。
ですので、console.log(value)ではtest1.txtの中身が出力されます。

まとめ

メソッドチェーンについて詳しく書きましたが、その他わからないことがあったらドキュメントを参考にしましょう。
http://azu.github.io/promises-book/