Help us understand the problem. What is going on with this article?

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

More than 5 years have passed since last update.

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を使うことで、ネストさせずに処理を記述していくことができます。

参考にしたサイト

cierpa
教育の機会平等をもたらすISAという後払いシステムを普及させようとしています。 現在は、圧倒的な実務経験を積むことを目的とした、ISAによる完全後払い制エンジニア学校『talentnest』と、その実務経験の元となる教材を作成するための、スタートアップ向けプロトタイプ開発『betastudio』の2つを運営しています。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした