嵌套在 async.js 瀑布中的异步函数

Posted

技术标签:

【中文标题】嵌套在 async.js 瀑布中的异步函数【英文标题】:Async function nested within async.js waterfall 【发布时间】:2014-05-09 03:20:40 【问题描述】:

免责声明:非工程师,对 JS 很陌生

大家好 - 我正在尝试利用 async.js 模块将一组函数链接在一起。我想要的输出是迭代mapData(对象数组),然后将其传递给最终函数(现在 - 只是 console.log(result)。

async.waterfall([
    function( callback ) 
        getCoords ( function( data ) 
            mapData = data;
        );
        callback(null, mapData);
    ,
    function( mapData, callback ) 
        //getEmail ( mapData );
        callback( null, mapData );
     
    ], function( err, result ) 
    console.log( result );
);

但是,getCoords 包含另一个异步函数(找到 here)。我看到的是第一个回调(null,mapData)在它返回之前发生,导致 null 结果。

我如何构造它以便getCoords 在继续下一个块之前返回mapData?我可能遗漏了一些非常明显的东西,谢谢!

【问题讨论】:

这就是你所做的一切,如果是这样,你为什么还需要异步,只需将getEmail 函数放在getCoords 回调中? @adeneo 这可能只是大型实现的一小部分,philsometypaway 需要一些东西来实现连续传递样式。 @JasonAller 是正确的 - 我需要将一堆这些链接在一起。谢谢你们的帮助! 【参考方案1】:

回调乐趣...您需要了解使用回调时程序流程是如何工作的。这可以通过一个非常简单的例子看出。

示例:

function doWork( callback ) 
  console.log( 2 );
  setTimeout(callback, 1000);


function master () 
  console.log(1);

  doWork(function () 
    console.log(3);
  );

  console.log(4);

master();

预期的结果是控制台日志的正确顺序是 1、2、3、4。但是在运行示例时,您会看到一些奇怪的东西,因为日志的顺序是 1、2、4、3。这是因为日志记录3 个发生在 doWork 完成之后,而 4 个记录发生在启动 doWork 之后,而不是等待它完成。

异步:

您可以使用异步库做很多事情,但要记住最重要的一点是回调函数始终将错误作为第一个参数接收,然后是您要传递的参数到列表中的下一个函数。

您链接到的gist 未设置为以这种方式返回。您可以修复它或在代码中处理它。首先让我们按原样使用函数:

使用现有的 getCoords:

async.waterfall([
  function( callback ) 
    // getCoords only returns one argument
    getCoords ( function( mapData ) 
      // First argument is null because there
      // is no error. When calling the waterfall
      // callback it *must* happen inside the getCoords
      // callback. If not thing will not work as you
      // have seen.
      callback( null, mapData);
    );
  ,
  function( mapData, callback ) 
    // Do work with the results of the 1st step
    // in the waterfall.

    // Finish the water fall
    callback( null, mapData );
   
], function( err, result ) 
  if ( err ) 
    console.log( err );
    return;
  
  console.log( result );
);

现在getCoords 有两个问题。首先是它没有向它的回调返回正确的参数,其次它并不总是使用它的回调。这第二个问题是巨大的,因为它会导致你的程序挂起和中断。

我已在函数中评论了已进行的 2 个修复。

固定 getCoords:

function getCoords ( callback )    
  var query = new Parse.Query( 'userGeoCoordinates' );
  query.exists( 'location' )
  query.find( 
    success: function ( result ) 
      for ( var i = 0; i < result.length; i++ ) 
        var object = result[ i ];
        var user = ;

        user.userId = object.get( 'userId' );
        user.coords = [];

        if ( !user_dedupe( user.userId ) ) 
                all_users.push( user );
        
      

      for ( var i = 0; i < all_users.length; i++ ) 
        for ( var j = 0; j < result.length; j++ ) 
          var object = result [ j ];
          if( object.get( 'userId' ) == all_users[ i ].userId ) 
            all_users[i].coords.push(
              [ object.get( 'location' )._longitude , object.get( 'location' )._latitude ]
            );
          
        

      
      // This is the original callback, let fix it
      // so that it uses the normal return arguments
      // callback( all_users );

      // Return null for no error, then the resutls
      callback( null, all_users );
    ,
    error: function( error ) 
      // Here is the second, bigger, issue. If the
      // getCoords had an error, nothing the callback
      // isn't called. Lets fix this
      //  console.log( 'error' );

      // Return the error, an no results.
      callback( error );
    
  );

修复getCoords 函数后,您可以简化瀑布:

第一个简化瀑布:

async.waterfall([
  function( callback ) 
    // getCoords returns the expected results
    // so just pass in our callback
    getCoords ( callback );
  ,
  function( mapData, callback ) 
    // Do work with the results of the 1st step
    // in the waterfall.

    // Finish the water fall
    callback( null, mapData );
   
], function( err, result ) 
  if ( err ) 
    console.log( err );
    return;
  
  console.log( result );
);

但是异步有一个很好的特性。如果您的瀑布步骤只是调用一个返回预期结果的函数,您可以使用 async.apply 进一步简化它。

第二个简化瀑布:

async.waterfall([
  async.apply( getCoords ),
  function( mapData, callback ) 
    // Do work with the results of the 1st step
    // in the waterfall.

    // Finish the water fall
    callback( null, mapData );
   
], function( err, result ) 
  if ( err ) 
    console.log( err );
    return;
  
  console.log( result );
);

【讨论】:

嗨,我是 javascript 的新手,我想问一个问题,在你的例子中你称之为 callback(null, mapData);或者说每个人都在我看到 async.waterfall 的最后使用回调()。我想知道回调函数的用途以及它在哪里定义 @rahul - 回调函数是作为瀑布的一部分为您定义的。它作为最后一个参数传递给数组中的每个函数。当你调用这个函数时,它基本上是在调用数组中的下一个函数。【参考方案2】:

尝试将瀑布的第一个函数的回调放在getCoords 回调中。这样,只有在 getCoords 回调设置了 mapData 后,才会调用瀑布中的第二个函数,此外,当您调用回调时,mapData 现在将在范围内。

async.waterfall([
    function( callback )               // F1
        getCoords ( function( data ) 
            mapData = data;
            callback(null, mapData);    // this is the entry point for F2
        );
    ,
    function( mapData, callback )      // now F2 is invoked only after mapData is set
        //getEmail ( mapData );
        callback( null, mapData );
    ], 
    function( err, result ) 
        console.log( result );
    );

【讨论】:

以上是关于嵌套在 async.js 瀑布中的异步函数的主要内容,如果未能解决你的问题,请参考以下文章

异步瀑布中带有 Mysql 连接池的 ForEach 函数

调用猫鼬保存方法时async.js挂起的瀑布方法

在瀑布中链接承诺

将异步函数添加到异步瀑布

未推送异步瀑布查询?

在 node.js 中使用异步瀑布