首页 > 请教一个关于Node 异步回调原理问题

请教一个关于Node 异步回调原理问题

我现在构造的代码如下:

exports.testHandler = function(callback){
    
    console.log('testHandler start');    
    var i = 0;    
    function iterator(item, cb){
        i++;                
        fs.readFile('E://file.txt','utf-8',function(err, data){
            cb(null);
        });
    }
        
    async.map(array, iterator, function(err, results){ 
        console.log('testHandler end');
        return callback(err);
    });
}

这个函数会在web项目中并发调用(比如10个http请求,同时触发这个函数),因此按node单线程异步的机制,当文件IO操作时,testHandler这个函数还会被继续调用。
我的问题是,当fs.readFile执行完回调时,系统如何记录这个回调,如何处理上面i这个局部变量?因为,如果是传统的单线程程序,函数调用给出的回调函数地址是唯一的(内存中代码段的某个地址),局部变量可以在相应的堆栈(函数调用栈)中去找。而node在回调的时候,它怎么知道要回调的是函数,局部变量对应的是哪次请求的?


感谢各位,我现在是试图这样去理解的,请各位指正:

对于javascript而言,同一个函数的多次调用会产生多个不同上下文(会有独立的调用栈),因此上面那个问题可以这样解释:
1、在node异步回调时,libeio返回触发的应该是一个函数指针(地址),同一个函数的多次调用其地址是不一样的;所以并发执行时,node回调的函数,虽然都叫XXX,但其实在内存里不是同一个。
2、每一个函数调用(就是上面提到的上下文)都有自己的调用栈,也就是说局部变量i会分别在各自调用栈中被分配资源。因此,即使在并发调用时,这些函数中的变量不会被相互影响。


个人认为题主的理解基本没有问题。

回调函数作为一个函数对象,在内存中只有一个。不同的是它每次被调用时都会产生一个新的执行上下文。作为回调函数,它每次被调用都占用一个单独的事件循环周期。

至于i等变量保存在哪儿,当然可以认为是保存在执行上下文中。回调函数执行完毕后,如果这些变量没有被其他函数引用,就可能被回收了。

另外,虽然你的http请求是并发的,但是它们对应的回调函数是一个一个串行执行的。这也正是JavaScript的重要特点。


知识点: 闭包。

引用 MDN 的解释:

答案在于:function(err, results) 成为了一个闭包(Closure)。所谓闭包,即一个函数以及其被创建的环境的合体。这里说的环境,包括了闭包创建时的所有可以被闭包访问到的局部变量。在这个例子中,function(err, results)就是一个结合了局部变量callback的闭包。


这个往深了讲就牵扯到node的底层libuv的实现问题,具体可以参考我的博文 js异步之惑

【热门文章】
【热门文章】