为什么这两种setTimeout执行顺序不一样

const obj = {
    name: " jsCoder",
    skill: ["es6", "react", "angular"],
    say: function ()
    {
        var that = this;
        for (var i = 0, len = this.skill.length; i < len; i++)
        {
            setTimeout((function()
            {
                console.log(i);
                console.log(that.skill[i]);
            })(), 0)
            console.log(i);
        }
    }
};
obj.say();

这段代码在Node里执行会报错,但可以在浏览器中执行,执行结果:

clipboard.png

第二种写法:

const obj = {
    name: " jsCoder",
    skill: ["es6", "react", "angular"],
    say: function () {
        var that = this;
        for (var i = 0, len = this.skill.length; i < len; i++) {
            (function () {
                var j = i;
                setTimeout((function () {
                    console.log(j);
                    console.log(that.skill[j]);
                }), 0)
            })()
            console.log(i);
        }
    }
};
obj.say();

clipboard.png

为什么,立即执行函数是异步任务吗?

阅读 2.2k
2 个回答

首先setTimeout是异步执行的,堆栈中碰到setTimeout会交给浏览器内核处理,等待setTimeout达到触发条件(即设定的时间),再返回给执行队列.先说说概念
第一种情况setTimeout里的function用立即执行函数,会直接执行。如果你把后面的()去掉你便会发现不会立即执行,而且后面输出的为undefine因为已经被回收啦
第二种情况setTimeout外加入了闭包,闭包中的变量会保存在内存中,并不会被垃圾回收机制回收。所以闭包中的i赋值给j后,j一直都存在,并未被回收,前面的0,1,2是主线程的i,后面的0,es6,1,react,2,angular则是setTimeout里输出的。立即函数不是异步的,setTimeout才是,交给浏览器内核处理再返回给执行队列的是setTimeout

你的setTimeout的写法是错的(如果我没猜错你的原意的话),setTimeout的第一个参数是一个回调函数,第二个参数是延迟执行的毫秒数。你的第一个参数虽然好像是一个函数,但你把这个函数用括号括起来,又在后面加了个括号立即去调用它,这样setTimeout的第一个参数遍被你设置成了这个函数的返回值而不是让setTimeout来帮你执行这个函数,从你写的这个函数来看,这个返回值应该是undefined,而undefined不是一个函数,它不是可调用的,我猜node里报的错应该是类似

TypeError [ERR_INVALID_CALLBACK]: Callback must be a function

at setTimeout (timers.js:425:11)

这样的错误吧?
正确的写法应该是:

setTimeout(function() {
  // Do something here
}, someTime);

换种说法就是,你需要传给setTimeout的第一个参数是一个函数指针/引用,而不是当场调用这个函数——这样你将把函数的返回值而不是这个函数本身作为第一个参数传入。

另外,setTimeout确实是异步的,并且现在推荐用setImmediate代替setTimeout(func, 0)

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题