LoginSignup
36
35

More than 5 years have passed since last update.

Node.jsでasyncモジュールを使って同期処理を行う

Last updated at Posted at 2015-06-23

Node.jsで同期処理を行う際にちょっとハマったのでメモ

やりたかったこと

  • Socket.ioのイベント内で、Redisにアクセスして、結果をゴニョゴニョして、Socket.ioでクライアントに結果を返す処理

ちょっとハマったポイント

  • Node.jsはノンブロッキングIOなので、関数が非同期に実行され、ゴニョゴニョする前の結果をSocket.ioで返却してしまっていた。

最初のソースコード

var redis = require("redis");
var socketIO = require("socket.io");
var express = require("express");
client = redis.createClient(port, 'ip');

//サーバー構築
var app = express();
var port = process.env.PORT || 5000;

app.use(express.static(__dirname + "/public"));
var server = http.createServer(app);
server.listen(port);

// Socket.IOサーバーを構築する
var io = socketIO.listen(server);

// websocketのイベント
io.sockets.on("connection", function (socket) {

 // イベントを受け取った時
 socket.on("event1", function (data) {

    //返却するリスト
    var returnArray = [];

    //Redisからデータを取得。ここでは複数のユーザープロフィールを取得したとする
    client.MGET(["userprofile1","userprofile2"], function(err, profiles){

        profiles.forEach(function (profile, i) {
            //ここでプロフィールをゴニョゴニョした結果をreturnArray に詰める
            returnArray.push(profile);
        });
    });

    //WebSocketで送信
    io.sockets.emit("retEvent1", returnArray);
  });

});

こんな感じで最初書いてましたw
こうすると、
returnArrayは空っぽのまま、WebSocketで送信されてしまいます。
Node.jsはノンブロッキングIOなので、client.MGETが完了するのを待たずに、
次のio.sockets.emitが実行されてしまうためです。
これは、やりたいことと違うので、asyncモジュールを使って、処理を同期的に書き換えます。
これくらいの処理では、async使わずともできますが、client.MGETの中でさらに、Redisにアクセスして
ゴニョゴニョしてってやっていくと、ネストがどんどん深くなり、可読性、メンテナンス性が下がるのでオススメしません。
asyncを使うと、ネストを深くせずに、処理を記述していけます。

ネストがどんどん深くなる例

client.MGET(["userprofile1","userprofile2"], function(err, profiles){


    client.GET("hoge", function(err, hogeval){
        // 何か処理

        client.GET("fugafuga", function(err, fugaval){
         // 何か処理

         });

    });

});

asyncを使ったソースコード

var redis = require("redis");
var socketIO = require("socket.io");
var express = require("express");

//async モジュールのインポート
var async = require('async');


client = redis.createClient(port, 'ip');

//サーバー構築
var app = express();
var port = process.env.PORT || 5000;

app.use(express.static(__dirname + "/public"));
var server = http.createServer(app);
server.listen(port);

// Socket.IOサーバーを構築する
var io = socketIO.listen(server);

// websocketのイベント
io.sockets.on("connection", function (socket) {

 // イベントを受け取った時
 socket.on("event1", function (data) {

    async.waterfall([
      function(callback) {
         client.MGET("userprofiles", function(err, profiles){
           //次の処理を呼び出す。callbackを呼ばないと次の処理は実行されない
           callback(null,profiles);
         });

      },
      function(profiles, callback) {
        var returnArray = [];
        profiles.forEach(function (profile, i) {
           //ここでプロフィールをゴニョゴニョした結果をreturnArray に詰める
           returnArray.push(profile);
       });

       callback(null,returnArray);

      },
     ],
     function(err, returnArray) { 
        //WebSocketで送信
        io.sockets.emit("retEvent1", returnArray);
   });

  });

});

async.waterfallを使うことで、ネストさせずに処理を記述していくことができます。

参考にしたサイト

36
35
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
36
35