Edited at

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

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


参考にしたサイト