搜索

首页  >  问答  >  正文

JavaScript 思考题:解决异步回调中的问题

引言

鉴于正在学习 NodeJS,在学习过程中我会不定期的发一些 javascript 思考题来和大家共同学习探讨,一方面可以加深对知识点的理解,另一方面也可从不同的角度来分析问题,希望大家踊跃回答吧~ :)

问题描述

有如下代码,希望能在回调中输出西游记中师徒四人的名字,可结果发现总输出最后一个人的名字:

function show(name, callback) {
  process.nextTick(function() {
    callback(name);
  });
}

var names = ["唐僧", "悟空", "八戒", "沙僧"];
for(var index in names) {
  var name = names[index];
  show(name, function() {
    console.log(name + "回来了");
  });
}

// 输出结果:
// 沙僧回来了
// 沙僧回来了
// 沙僧回来了
// 沙僧回来了

试问应该怎么修改代码,使之正确输出?(show 方法不能改)

迷茫迷茫2776 天前386

全部回复(5)我来回复

  • 巴扎黑

    巴扎黑2017-04-10 17:53:06

    很简单的一个修改方法是,将下面代码:

    show(name, function() {
        console.log(name + "回来了");
      });

    中的函数加一个参数name即可。即

    show(name, function(name) {
        console.log(name + "回来了");
      });

    2016.03.22补充】针对题主自己的回答的评论:

    其实你的两种改法都用到了闭包。区别在于

    • 第一种是用了show函数与nextTick包围的函数形成的闭包。

    • 第二种很明显了,是用了你新增的那个函数与外部函数形成的闭包。

    我觉得第一种改法更自然一下,因为原来的代码里面调callback时传了参数,但却没有使用,所以加上name是很合理的。而且本来的代码就能够形成闭包了,第二种改法却又在其他地方构造闭包,有点多余。

    总结一下:要让结果运行正确,就必须要使数组中四个不同的元素各自保存到独立的作用域中。不论你的这两种改法还是通过ES6的let,都是这个目的。只不过方法不同而已。

    回复
    0
  • 巴扎黑

    巴扎黑2017-04-10 17:53:06

    典型闭包坑

    for(var index in names) {
      var name = names[index];
      (function(name){
      show(name, function() {
        console.log(name + "回来了");
      });
      })(name);
    }

    https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures#Creating_closures_in_loops_A_common_mistake

    回复
    0
  • PHP中文网

    PHP中文网2017-04-10 17:53:06

    补充:我也觉得楼主的题给错了,回调函数调用时都没传到参数,show的定义里是有的。

    推特上看到的图,说的是setTimeout的情况,可以参考:
    方法1:使用ES6 的let声明

    方法2:使用IIFE 传参,函数参数是按值传的,因此i值保留
    方法3:使用bind绑定参数

    回复
    0
  • 阿神

    阿神2017-04-10 17:53:06

    // 方法一
        for(var index in names) {
          var name = names[index];
          (function(name){
            show(name, function() {
              console.log(name + "回来了");
            });
          })(name);
        }
    
    // 方法二
        for(var index in names) {
          var name = names[index];
          show(name, function(name) {
            console.log(name + "回来了");
          });
        }
    

    @hsfzxjy @manxisuo 其实我最想了解的就是这样两种方法的本质区别在哪里?如果方法二能解决这个问题,那么方法一所谓的闭包坑应该怎么去理解?只是方法二的另一种实现形式吗?

    @lijsh 小胖你给出的是 ES6 的一些解法,确实很有参考价值,只是我还是没搞明白以上问题

    回复
    0
  • 迷茫

    迷茫2017-04-10 17:53:06

    function show(name, callback) {
      process.nextTick(function() {
        callback(name);
      });
    }
    

    改成

    function show(name, callback) {
     // process.nextTick(function() {
        callback(name);
     // });
    }
    

    回复
    0
  • 取消回复