【问题标题】:How are node.js callbacks asynchronous?node.js 回调如何异步?
【发布时间】:2012-06-03 08:50:46
【问题描述】:

我最近开始使用 node.js。它引用了 node.js 异步行为可以以三种方式使用events、stream、callback。

事件和流在 emit,pipe 事件上工作,使其真正异步,但回调异步如何,因为它在函数返回之前 执行,除非 process.nextTick() 被使用。

事件:

event.on('data',function(data){

});

回调:

function(data,cb){
 // do something with "data"
 cb();
 return;   
}

【问题讨论】:

    标签: javascript node.js


    【解决方案1】:

    让我们仔细看看有回调的函数:

    function(data, cb) {
     // do something with "data"
     cb();
     return;
    }
    

    这里有两个大错误:

    1) 当你有一个回调函数时,你没有理由使用 return。 Return 仅在您具有立即发送回响应的同步操作时使用(它们不必等待将来某个时间触发回调)。

    2) 你不会立即执行回调,这没有任何意义。您要么不使用回调并使用 return 语句(当您实际上没有异步函数时),要么在将来某个时间执行回调,当您得到结果时。

    更好的例子:

    function (data, cb) {
      // you make an Ajax call and 
      // you don't know when the callback will be triggered
      makeAjaxCall(data, cb);
    }
    

    【讨论】:

    • 您可以通过异步操作非常合理地返回某些内容,例如您可能会返回 this 以允许方法链接。
    • @lanzz:不过听起来像是比赛条件的绝佳秘诀。
    • @MichaelBorgwardt 我认为在事件驱动的单线程代码中导致竞争条件是非常人为的。另外,object.method1().method2() 比 object.method1(); object.method2() 更容易出现竞争条件?
    • 抱歉有很多错误。我认为问题被误解了。示例只是为了后代。 @alessioalex:函数不是同步的,只是将cb 作为范围传递,主函数在makeAjaxCall 完成之前无法返回,但是到那时callback 已经被调用,如果它不是基于事件的或者我们将它延迟到nextTick。我见过很多节点模块在回调后使用 return 进行链接。
    【解决方案2】:

    你说得对,回调不一定意味着异步 I/O。

    事件也不一定是异步的,因为 EventEmitter 的 .emit 立即起作用,而不是在下一个滴答声:

    var EventEmitter = require('events').EventEmitter;
    
    var emitter = new EventEmitter();
    
    emitter.on('ev', function(x) { 
        console.log("received event ", x);
    });
    
    console.log("Hello");
    emitter.emit('ev', "X");
    console.log("World");
    

    结果:

    Hello
    received event  X
    World 
    

    但是,回调可以通过以下方式“异步”:

    • 使用 process.nextTick()。这不会使回调中的实际 I/O 操作异步。它只是将执行推迟到下一个刻度。如果某些操作阻塞,事件循环也会阻塞 - 在下一个滴答而不是这个滴答。

      function(data, cb) {
          process.nextTick(function() { doSomethingWith(data); cb(); })
      });
      
    • 从已知由异步 I/O 操作触发的事件中调用指定的回调函数

      function(data, cb) {
          emitter.startIO();
          emitter.on('someIOEvent', function(e) { 
              doSomethingWith(data,e); 
              cb(); 
          });
      });
      
    • 使用另一个已知为异步的基于回调的函数。节点核心和节点模块中的大部分功能都是这样的。

      function(data, cb) {
          otherFunction(data, function(moredata) { 
              doMoreStuffWith(moredata, data); cb(); 
          });
      });
      

    【讨论】:

      【解决方案3】:

      在这种情况下,带有回调的函数不是异步运行的,但是仍然存在可以异步使用此类函数的情况:

      function foo(data, callback) {
          // do something with the data
          callback();
      }
      
      function bar() {
          // this will be called as a callback from foo()
      }
      
      setInterval(function() { foo(data, bar) }, 1000);
      console.log('this is displayed immediately');
      

      您可以看到foo() 计划每秒运行一次,并且这是异步发生的;但是,回调的存在允许您从 foo() 函数之外设置额外的行为,例如您可能会设置几种情况,使用不同的回调调用foo(),从而导致不同的应用程序逻辑。

      【讨论】:

        猜你喜欢
        • 2017-03-20
        • 2013-12-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-06-04
        • 2013-05-15
        • 2018-11-24
        相关资源
        最近更新 更多