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返回的数据是空,也就是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,这里不多加分析。

@Test public void asyncThread()throws Exception{ CompletableFuture async1 = CompletableFuture.runAsync(()->{ try { Thread.sleep(1000); System.out.println(Thread.currentThr 问题:解决 forEach 循环无法正确处理异步操作和等待操作的完成 解决方案:1.使用 for...of 循环 2.使用 for 循环 3.使用 Promise.all + map 点击上方“Java基基”,选择“设为星标”做积极的人,而不是积极废人!源码精品专栏原创 | Java 2020超神之路,很肝~文详细注释的开源项目RPC 框架 Dubbo 源码解析... // Map.entrySet来遍历key,value, 大容量时推荐使用 map.entrySet().forEach(entry -> { System.out.println(entry.getKey()); 1.实体类取出某个字段并收集成list 例:传回的是list<实体类>,根据实体类的id能查询表,这时候普通的就for循环 //x就代表每一个实体类、也是循环的实体类 // UserList为实体类的集合 List<User> userList.stream().map(x -> x.getId).collect(Collectors.toList()); 上一篇文章,讲述了Future模式的机制、缺点,CompletableFuture产生的由来、静态工厂方法、complete()方法等等。本文将继续整理CompletableFuture的特性。3.3 转换我们可以通过CompletableFuture来异步获取一组数据,并对数据进行一些转换,类似RxJava、Scala的map、flatMap操作。3.3.1 map方法名描述thenApply(... 在JS的 for、foreach、$.each等等的循环,本身是不存在阻塞模式的,以下方法本质的原因,是因为匿名函数使用循环体的变量的优先级最高,而普通方式访问的对变量优先级是按代码本身的逻辑顺序的,对变量的访问在循环之后,所以得到的i是循环执行完之后的i,所以不是正确结果; 然而,直接执行匿名函数的方式当场就拿到了正确的i,闭包的方式也是因为将外部函数的变量保存在内存,从而可以得到正确的... forEach同/异步问题一、forEach外部等待forEach执行完成二、forEach内部等待异步执行完成三、既需要forEach内部同步执行,又需要forEach外部同步执行 一、forEach外部等待forEach执行完成 let arr = [1, 2, 3, 4, 5, 6, 7]; let arr2 = []; arr.forEach((item) => { setTimeout(() => { arr2.push(item); }, 1000); console.log(sum) // 此处输出为0,而不是4,如果这时候后续操作需要用到sum等于4的值,那么就需要修改为如下写法 async getSum() { const temp = [1,2,3,4,5,6,7,8] let this.sum = 1.1 正常方式循环 Map 执行结果:1.2 forEach 循环 Map 执行结果:1.3 对于Map 包含的键或值 null ,forEach 将打印 null。 执行结果:1.4 如果不想打印 key=null 的值,可以在 forEach 指定 执行结果:2.1 正常方式循环列表 执行结果:2.2 Java 8 ,可以使用 forEach 循环一个 List 执行结果:2.3 过滤 List 的 null 值 执行结果:查看方法签名,它接受一个功能接口 Consumer 这个例子创建了 public static void main(String[] args) { Map<String, String> map = new HashMap<String, String>(); map.put("a", "1"); map.put("b", "2"); map.put("c", "3... jsmap方法内使用异步(调用接口)jsmap方法内使用异步(调用接口) 2019/08/13 jsmap方法内使用异步(调用接口) map方法执行的是同步函数,若是想要使用异步,可以使用关键字async await 直接贴代码(我使用的是axios库调用接口) arr.map( async (item, index) => {//函数使用async关键字