您的位置:首页 > 数据库

异步方法中map、forEach和for循环中带来的异步执行问题

2016-08-26 17:32 399 查看
关键词:map() forEach() for() 异步执行 res.jsonp()

CSDN个人博客:http://blog.csdn.net/sam976

问题描述

在map循环中使用mongoose的Model.findOne查询mongodb数据库,查询到数据后使用res.jsonp响应。

代码如下:

router.get('/getData', function(req, res) {
var data=[1,2,3];
var result=data.map(function(v, i, a) {
/*根据studentid到数据库中查询数据*/
Fee.findOne({
studentid: "57b525bc4e8d464803167da7"
}, {}, function(err, fees) {
if (err) return handleError(err);
var array_test = [];
array_test[i] = {};
array_test[i].number = v;
array_test[i].fees = fees;
/*打印查询结果*/
console.log('++++++array_test[i]++++++')
console.log(array_test[i])
console.log('++++++array_test[i]++++++')
return array_test[i];
});
});
/*result被赋予查询结果*/
console.log('-----result-----');
console.log(result);
console.log('-----result-----');
res.jsonp({
one: 'aa',
two: 'bb',
three: result
});
});


页面发送请求http://127.0.0.1:3000/getdata时,jsonp返回的数据是空,也就
4000
是result为空。

结果如图所示:



首先可以肯定,查询结果不为空,因为查询条件是专门设定的。

我们知道Array.forEach .map是同步(阻塞)的,也就是在循环结束后,后面的语句才会被执行,如果按照这个逻辑,jsonp返回的result不会为空,那问题出在哪里?

问题分析

经过分析,查询结果确实不为空,这个可以从服务器控制台打印结果看出。如图黄色部分即为被赋予查询结果的array_test[i]:



这时候,会发现result打印的位置(红框部分)居然在array_test[i]结果之前,这和代码的顺序不符合呀!它变成了异步执行。

通过查询mongoose文档可以发现,Model.findOne([conditions], [projection], [options], [callback])是一个异步执行的函数,有结果就会调用callback。而node中的map()、forEach()、for()循环有一个特性:当其函数里面里面有回调它就变成异步。map就变成了这样,第一次循环开始,findOne查询数据库,不等有结果就开始第二次查询开始,…第三次查询开始,循环结束,不等有结果它就开始执行console.log(result),所以result为空,而且打印结果在array_test[i]之前。

为了证明分析对不对,我简单测了一下,既然findOne是异步函数,那么如果我在map中不用异步函数,是不是打印顺序和代码书写顺序就是一样的?

代码如下:

router.get('/getData', function(req, res) {
var data = [1, 2, 3];
var result = data.map(function(v, i, a) {
var array_test = [];
array_test[i] = {};
array_test[i].number = v;
array_test[i].fees = 'cc';
return array_test[i];
});
console.log('-----result-----');
console.log(result);
console.log('-----result-----');
res.jsonp({
one: 'aa',
two: 'bb',
three: result
});
});


结果如下:

jsonp返回的result不为空



result的书序也和代码中书写顺序一样



到这里就可以看出,果然是异步的问题导致result为空。那怎么解决?

解决方法

可以用async的map来处理这种情况,async是nodejs的一个组件,用来解决node的异步问题。而其中的map方法:

对集合中的每一个元素,执行异步操作,得到结果。所有的结果将汇总到最终的callback里。其实就是把异步都加到队列里, 等全都执行完了之后执行一个统一的回调,这样看起来就是同步的效果。

代码如下:

/*这部分代码和前面代码不太一样,单纯只是为了演示async.map的效果如何。*/
router.get('/getData', function(req, res) {
var data = [1, 2, 3];
var array_test = {};
async.map(data, function(v, callback) {
/*根据studentid到数据库中查询数据*/
Fee.findOne({
studentid: "57b525bc4e8d464803167da7"
}, {}, function(err, fees) {
if (err) return handleError(err);
array_test[v]=fees.studentid;
console.log('++++++array_test++++++')
console.log(array_test);
console.log('++++++array_test++++++')
callback(null, array_test);
});
}, function(err, results) {
console.log('-----result-----');
console.log(results);
console.log('-----result-----');
res.jsonp({
one: 'aa',
two: 'bb',
three: results
});
});
});


页面效果如下图,可以看到result有数据:



服务器控制台效果如下图,可以看到array_test打印在result前面,是同步的顺序:



更多关于async请查看官方文档或者github中文文档

当然也有其他的方式解决这个问题,比如bluebird、asyncawait,这里不多加分析。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  异步 数据库