JavaScript 思考题:解决异步回调中的问题
迷茫
迷茫 2017-04-10 17:51:06
0
5
384

引言

鉴于正在学习 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 方法不能改)

迷茫
迷茫

业精于勤,荒于嬉;行成于思,毁于随。

reply all(5)
巴扎黑

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

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

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

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

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

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

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

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

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

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

巴扎黑

典型闭包坑

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

Ty80

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

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

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

阿神
// 方法一
    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 的一些解法,确实很有参考价值,只是我还是没搞明白以上问题

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

改成

function show(name, callback) {
 // process.nextTick(function() {
    callback(name);
 // });
}
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template