async的concatSeries循环里面,嵌套回调函数同步问题【已解决,感谢jiangmiao同学】
发布于 11 年前 作者 yandaoyong 6638 次浏览 最后一次编辑是 8 年前
         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位置即可】

17 回复

所有的循环都可以改写其异步形式,如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 >= 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这个回调

@yandaoyong 如果你执行的taskAttribute是异步,那么整个循环就是异步。

@yandaoyong 如果要让callback在异步taskAttribute后执行,callback肯定要存在于taskAttribute的回调函数中。解决方法参见我的回答。要让包含异步循环顺序执行必须用异步的方式写循环。

@jiangmiao

我循环用的是concatSeries,这个方法是同步挨个执行的

@jiangmiao 怎么改一下taskAttribute,让它和callback同步?

@yandaoyong 同步的话callback就不会执行在taskAttribute之前。

@yandaoyong 如果要让callback在异步taskAttribute后执行,callback肯定要存在于taskAttribute的回调函数中。

@yandaoyong

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
                };
                // 提进来!
                callback(null,data[taskinfo]);
            });

@yandaoyong 哪里是异步的终点写哪里,如果终点被隐藏,重写代码。

@jiangmiao

感谢,搞定

那么简答,真为自己惭愧啊

@yandaoyong 客气,我也没看仔细看问题,看到你另一个循环的贴子就回了个循环异步的变换,异步流程上的所有问题都可以用类似的方法拆解改写。

@jiangmiao 上次你说到循环改写异步的问题, 我来自卖自夸了. 楼主另一个帖子里 (话说那个帖子删了? 我只好贴这里来了见谅) 那段代码改写成 Stekinscript 异步管道形式如下 (# 号之后是注释, 对比原有的 js 代码)

import handler, taskDao

handler.myTasklist: (msg, session, next):      # handler.myTasklist = function(msg,session,next){
  uid: session.uid                             #   var uid = session.uid;
  taskDao.myTasklist(uid, (error, res):        #   taskDao.myTasklist(uid,function(err,res){
                                               #     var tasks = [];
    tasks: res |:                              #     for(var i=0;i<res.length;i++){
                                               #       var task = res[i];
                                               #       (function(tasks,task){
      taskDao.taskAttribute($.id, %(err, res)) #         taskDao.taskAttribute(task.id,function(err,res){
                                               #           var attributes = []
      attributes: res |: {                     #           for(var j=0;j<res.length;j++){
                                               #             var attribute = {
        title: $.title,                        #               title:res[j].title,
        attributevalue: $.attributevalue,      #               attributevalue:res[j].attributevalue
                                               #             };
                                               #             attributes.push(attribute);
      }                                        #           }
      $result.push({                           #           var taskinfo = {
        title: $.title,                        #             title:task.title,
        type: $.title,                         #             type:task.type,
        status: $.status,                      #             status:task.status,
        attributes: attributes,                #             attributes:attributes
                                               #           };
                                               #           tasks.push(taskinfo);
      })                                       #         });
                                               #       })(tasks,task);
                                               #     }
    next(null, {                               #     next(null,{
      route: 'onMytasklist',                   #       route: 'onMytasklist',
      tasks: tasks,                            #       tasks: tasks
    })                                         #     });
  )                                            #   });
                                           上面那段代码    # };

Stekinscript 最新分支编译, 产生等价于 (之所以说等价于, 是删去一些无用语句并且进行代码格式化之后的结果) 下面的 js 代码, 将管道也就是循环中的异步改写成递归, 并且将之后的语句 (next 调用, 因为 stekinscript 会作一些 name mangle 因此生成的代码中叫做 $c_next) 放在递归的末尾执行

(function () {
    handler.myTasklist = (function ($c_msg, $c_session, $c_next) {
        var $c_uid;
        $c_uid = $c_session.uid;
        taskDao.myTasklist($c_uid, (function ($c_error, $c_resA) {
            var $c_tasks;
            (function ($list) {
                if (!($list) || $list.length === undefined) throw 'not iterable';

                function $next($index, $result) {
                    if ($index === $list.length) {
                        $c_tasks = $result;
                        $c_next(null, ({
                            "route": "onMytasklist",
                            "tasks": $c_tasks
                        }));

                    }
                    else {
                        var $element = $list[$index];
                        var $c_attributes, $c_err, $c_res;
                        taskDao.taskAttribute($element.id, (function ($tp_err, $tp_res) {
                            $c_err = $tp_err;
                            $c_res = $tp_res;
                            $c_attributes = (function ($list) {
                                if (!($list)) return;
                                var $result = [];
                                var $ind = 0;
                                var $next = function () {};
                                var $pipeRet;
                                for (var $k in $list) {
                                    if ($ind === $list.length) {
                                        break;
                                    }
                                    $pipeRet = (function ($index, $key, $element) {
                                        $result.push(({
                                            "title": $element.title,
                                            "attributevalue": $element.attributevalue
                                        }));

                                        return [0];
                                    })($ind, $k, $list[$k]);
                                    switch ($pipeRet[0]) {
                                        case 1:
                                            return $pipeRet[1];
                                    }++$ind;
                                }
                                return $result;
                            })($c_res);
                            $result.push(({
                                "title": $element.title,
                                "type": $element.title,
                                "status": $element.status,
                                "attributes": $c_attributes
                            }));
                            return $next($index + 1, $result);
                        }));

                    }
                }
                $next(0, []);
            })($c_resA);
        }));
    });
})();

请多指教.

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

可以参考 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

@jiangmiao 惭愧, 还没这种支持. 很有意思的样子, 得看看生成的 js 代码是怎么布置的.

回到顶部