【node.js】非同期処理

node.js で何回かHTTP通信をしたときの対応。

node.jsはシングルスレッドで動作します。
さらにノンブロッキングなので同期的な処理は基本しません。
なのでHTTP通信を行って、結果を待って、次の処理というのは
普通に書くとネスト地獄になりコードの可読性が著しく悪くなります。
ということで今回は「Promise」を使用しました。

本当は「sync-request」を使おうと思ったのですが
非推奨?なのかあまり使うなと書かれていたので、今回はパス!

あと一応命名や文法も気にはしてますが、完全に守れてはいないのあしからず・・・
http://cou929.nu/data/google_javascript_style_guide/
http://popkirby.github.io/contents/nodeguide/style.html

何も考えないで実装した場合

app.js

const https         = require ('https');
const url           = require('url');
const ACCESS_URL    = 'https://www.itcowork.co.jp/';
const opt           = url.parse(ACCESS_URL);
opt.method          = 'GET';

console.log('START');

// 変数を宣言
var code;
var body;

var req = https.request(opt, (res) => {
    console.log('status code : ' + res.statusCode);
    
    code = res.statusCode;
    body = '';

    res.on('data', (chunk) => {
        body += chunk;
        console.log('data');
    });

    res.on('end', () => {
        console.log('end');
    });
    
})

req.on('error', (err) => {
    console.log('request error : ' + err.message);
});

req.end();

console.log('LAST CHECK : ' + code);

console.log('END');

START
LAST CHECK : undefined
END
status code : 200
data
end

先に「LAST CHECK」が出てあとからHTTP処理の結果が出ています。
1個だけHTTP処理をする分にはこれでも大丈夫です。
もしくは「end」の処理の部分に別Functionを呼び出してネストしまくるとか
でも一応は対応できます。

が、今回はもっとスマートに書きたいので「Promise」を使用します。

Promiseを利用した場合

app.js

const https         = require ('https');
const url           = require('url');
const ACCESS_URL    = 'https://www.itcowork.co.jp/';
const opt           = url.parse(ACCESS_URL);
opt.method          = 'GET';

console.log('START');

test1().then((value) => {

    console.log('status code : ' + value);
    console.log('END');

}, (err) => {
    console.error("error:", err.message);
});


function test1(){

    return new Promise ((resolve, reject) => {
        var req = https.request(opt, (res) => {
            console.log('1.status code(function) : ' + res.statusCode);
            
            var code = res.statusCode;
            var body = '';
        
            res.on('data', (chunk) => {
                body += chunk;
                console.log('1.data');
            });
        
            res.on('end', () => {
                console.log('1.end');
                resolve(code);
            });
            
        })
        
        req.on('error', (err) => {
            console.log('1.request error : ' + err.message);
            reject(err)
        });
        
        req.end();
    }); 
}

START
1.status code(function) : 200
1.data
1.end
status code : 200
END

Promiseオブジェクトを作成して、thenで実行し実行結果を取得しました。
正しい順序でログが出てることがわかります。

複数回HTTP通信

次に複数回のHTTP通信の実装です。
お試しなのでHTTP通信先はまったく同じですごめんなさいm(_ _)m

app.js

const https         = require ('https');
const url           = require('url');
const ACCESS_URL    = 'https://www.itcowork.co.jp/';
const opt           = url.parse(ACCESS_URL);
opt.method          = 'GET';

console.log('START');

var promise = Promise.resolve();
promise
    .then(test1)
    .then((result) => {
        return new Promise(function(resolve, reject){
            console.log("1.status code : " + result);
            resolve(1);
        });
    })
    .then(test2)
    .then((result) => {
        return new Promise(function(resolve, reject){
            console.log("2.status code : " + result);
            resolve(2); 
        });
    })
    .catch(onRejectted);


function test1(){

    return new Promise ((resolve, reject) => {
        var req = https.request(opt, (res) => {
            console.log('1.status code(function) : ' + res.statusCode);
            
            var code = res.statusCode;
            var body = '';
        
            res.on('data', (chunk) => {
                body += chunk;
                console.log('1.data');
            });
        
            res.on('end', () => {
                console.log('1.end');
                resolve(code);
            });
            
        })
        
        req.on('error', (err) => {
            console.log('1.request error : ' + err.message);
            reject(err)
        });
        
        req.end();
    }); 
}

function test2(){

    return new Promise ((resolve, reject) => {
        var req = https.request(opt, (res) => {
            console.log('2.status code(function) : ' + res.statusCode);
            
            var code = res.statusCode;
            var body = '';
        
            res.on('data', (chunk) => {
                body += chunk;
                console.log('2.data');
            });
        
            res.on('end', () => {
                console.log('2.end');
                resolve(code);
            });
            
        })
        
        req.on('error', (err) => {
            console.log('2.request error : ' + err.message);
            reject(err)
        });
        
        req.end();
    }); 
}

function onRejectted(error) {
    console.log("error:" + error);
}

START
1.status code(function) : 200
1.data
1.end
1.status code : 200
2.status code(function) : 200
2.data
2.end
2.status code : 200

もし「test1」と「test2」のFunctionに引数を渡す場合は
以下のように呼び出せば正しく動作します。

var promise = Promise.resolve();
promise
    .then((result) => {
        return test1(1);
    })
    .then((result) => {
        return new Promise(function(resolve, reject){
            console.log("1.status code : " + result);
            resolve(1);
        });
    })
    .then((result) => {
        return test2(2);
    })
    .then((result) => {
        return new Promise(function(resolve, reject){
            console.log("2.status code : " + result);
            resolve(2); 
        });
    })
    .catch(onRejectted);

途中でエラーにしたい場合は以下のようにすればOKのはず

var promise = Promise.resolve();
promise
    .then(test1)
    .then((result) => {
        return new Promise(function(resolve, reject){
            console.log("1.status code : " + result);
            if(result == 404) reject(new Error("わざとエラー"));
            //resolve(1);
        });
    })
    .then(test2)
    .then((result) => {
        return new Promise(function(resolve, reject){
            console.log("2.status code : " + result);
            resolve(2); 
        });
    })
    .catch(onRejectted);
START
1.status code(function) : 404
1.data
1.end
1.status code : 404
error:Error: わざとエラー

まとめ

久しぶりに複数処理を書きましたが、
非同期とか久しぶりすぎて忘れてました!
そして未だにアロー関数(=>)に慣れませんw

ちなみにソース中でHTTPアクセスしてるのは
前職のWEBサイトです。怒られたら変更しよう!

以上

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください