Nodejs async的concatSeries循环里面,嵌套回调函数同步问题【已解决,感谢jiangmiao同学】

Nodejs async的concatSeries循环里面,嵌套回调函数同步问题【已解决,感谢jiangmiao同学】

         handler.myTasklist = function(msg,session,next){
var uid = session.uid;
taskDao.myTasklist(uid,function(err,res){
//res为一个数组,这里使用async的concatSeries方法,同步依次执行
async.concatSeries(res,function(task,callback){
var taskinfo;
//这里出现问题,直接跳过了taskDao,直接进入callback(null,data[taskinfo])了,正确的方法该怎样写呢?
taskDao.taskAttribute(task.id,function(err,res){
var attributes = []
for(var j=0;j<res.length;j++){
var attribute = {
title:res[j].title,
attributevalue:res[j].attributevalue
};
attributes.push(attribute);
}
taskinfo = {
title:task.title,
type:task.type,
status:task.status,
attributes:attributes
};
});
//将得到的taskinfo传入回调函数,由于没有同步taskDao.taskAttribute,直接导致这里到taskinfo为空
callback(null,data[taskinfo]);
},function(err,values){
console.log(“tasks:”+values);
next(null,{
route: ‘onMytasklist’,
tasks:values
});	
});
});

};

要求就是:for循环里面 先执行taskDao.taskAttribute,取得taskinfo后 再执行下面的callback(null,data[taskinfo]);这个回调

希望不要嫌弃新人技术哈

【已解决,改变一下callback位置即可】


6 回复

Nodejs async的concatSeries循环里面,嵌套回调函数同步问题【已解决,感谢jiangmiao同学】

在处理异步操作时,特别是涉及到数据库查询或者网络请求时,经常会遇到回调函数嵌套的问题。在你的例子中,你使用了async.concatSeries来依次执行一系列任务,并且每个任务都涉及到了异步数据库查询。你需要确保在每次查询完成后,再继续执行后续的回调函数。

原始代码中的问题

原始代码中的问题是,在调用taskDao.taskAttribute之后,直接执行了callback(null, data[taskinfo]),而没有等待taskDao.taskAttribute完成。这会导致taskinfo为空,因为异步操作还没有完成。

解决方案

解决方案是将callback(null, taskinfo)放在taskDao.taskAttribute的回调函数内部,以确保在查询完成后再执行回调。

handler.myTasklist = function (msg, session, next) {
    var uid = session.uid;
    taskDao.myTasklist(uid, function (err, res) {
        // 使用async的concatSeries方法,同步依次执行
        async.concatSeries(res, function (task, callback) {
            var taskinfo;

            // 调用异步方法获取task信息
            taskDao.taskAttribute(task.id, function (err, res) {
                if (err) {
                    return callback(err); // 如果有错误,直接传递给回调
                }

                var attributes = [];
                for (var j = 0; j < res.length; j++) {
                    var attribute = {
                        title: res[j].title,
                        attributevalue: res[j].attributevalue
                    };
                    attributes.push(attribute);
                }
                taskinfo = {
                    title: task.title,
                    type: task.type,
                    status: task.status,
                    attributes: attributes
                };

                // 将taskinfo传递给回调函数
                callback(null, taskinfo);
            });
        }, function (err, values) {
            if (err) {
                console.error("Error:", err);
                return next(err);
            }

            console.log("tasks:", values);
            next(null, {
                route: 'onMytasklist',
                tasks: values
            });
        });
    });
};

解释

  1. 异步操作taskDao.taskAttribute 是一个异步操作,它需要一段时间才能返回结果。
  2. 回调函数:在 taskDao.taskAttribute 的回调函数内部,我们构建了 taskinfo 并将其传递给外部的 callback 函数。
  3. 错误处理:如果在 taskDao.taskAttribute 中发生错误,我们可以直接通过 callback(err) 传递错误。
  4. 最终回调async.concatSeries 的最终回调函数会在所有任务完成后被调用,并将所有 taskinfo 组合成一个数组传递给 next

通过这种方式,你可以确保每个异步操作都按顺序完成,从而避免了回调地狱(callback hell)的问题。


所有的循环都可以改写其异步形式,如for循环

function foo(items)
{
	for (var i=0; i<items.length; ++i)
	{
		something();
	}
	another();
}

的异步版本则是

function foo(items, cb)
{
	var i, once, next, done;
i = 0;

once = function() {
	if (i &gt;= items.length)
		return done();

	item = items[i];
	process.nextTick(function() {
		something();
		next();
	});
};

next = function() {
	++i;
	once();
};

done = function() {
	another();
	cb();
};

return once();

}

如果条件简单,也可合并写成

function foo(items, cb)
{
    var i, once;

    i = -1;

    once = function() {
        if (++i >= items.length) {
            another();
            return cb();
        }

        item = items[i];
        process.nextTick(function() {
            something();
            once();
        });
    };

    once();
}

不好意思,我是需要同步

就是for循环里面,先执行嵌套的taskDao.taskAttribute这个回调函数,然后再执行callback

现在的问题是,直接跳过了嵌套的taskDao.taskAttribute这个回调函数

要求就是:for循环里面 先执行taskDao.taskAttribute,取得taskinfo后 再执行callback这个回调

很好很强大,如果循环中要并发而不是顺序执行不知是否支持。

可以参考 IcedCoffeeScript的话await for为并发,for … await 则为顺序。

比如

await for i in [0..2]
	# 并发
	((i, cb) ->
		# 顺序执行
		for j in [0..2]
			await setTimeout defer(), 500
			console.log "#{i} #{j}"
		cb()
	)(i, defer())
console.log 'done'

输出为

# 3并发
0 0
1 0
2 0
# 等待 500 ms
0 1
1 1
2 1
# 等待 500 ms
0 2
1 2
2 2
done

在你的原始代码中,callback(null, data[taskinfo]) 这一行被执行得太早了。因为在 taskDao.taskAttribute 的回调函数内部(即异步操作完成之后)之前,taskinfo 还没有被正确地赋值,所以你需要将 callback 放置在 taskDao.taskAttribute 的回调函数内部。这样可以确保 taskinfo 在被传递给 callback 之前已经被正确赋值。

以下是修正后的代码示例:

handler.myTasklist = function (msg, session, next) {
    var uid = session.uid;
    taskDao.myTasklist(uid, function (err, res) {
        if (err) {
            return next(err);
        }

        async.concatSeries(res, function (task, callback) {
            taskDao.taskAttribute(task.id, function (err, res) {
                if (err) {
                    return callback(err);
                }

                var attributes = [];
                for (var j = 0; j < res.length; j++) {
                    var attribute = {
                        title: res[j].title,
                        attributevalue: res[j].attributevalue
                    };
                    attributes.push(attribute);
                }

                var taskinfo = {
                    title: task.title,
                    type: task.type,
                    status: task.status,
                    attributes: attributes
                };

                // 将 taskinfo 传递给 callback
                callback(null, taskinfo);
            });
        }, function (err, values) {
            if (err) {
                return next(err);
            }
            
            console.log("tasks:" + values);
            next(null, {
                route: 'onMytasklist',
                tasks: values
            });
        });
    });
};

在这个修正后的版本中,callback(null, taskinfo) 被放置在 taskDao.taskAttribute 的回调函数内部。这确保了 taskinfo 在被传递给 async.concatSeriescallback 函数之前已经被正确赋值。

回到顶部