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

时间:2014-03-29 16:19:12

标签: javascript node.js asynchronous async.js

免责声明:非工程师,JS的新手

嘿所有 - 我试图利用async.js模块将一组函数链接在一起。我想要的输出是迭代mapData(对象数组),然后将它传递给最终函数(现在 - 只是console.log(结果)。

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?我可能错过了一些非常明显的东西,谢谢!

2 个答案:

答案 0 :(得分:8)

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

示例:

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。这是因为日志记录在doWork完成后发生3,而在启动doWork后发生4记录,而不是等待它完成。

<强>异步:

您可以使用异步库做很多事情,但大多数要记住的重要事情是回调函数总是接收错误,因为第一个参数后跟您要传递的参数到列表中的下一个功能。

您链接到的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存在两个问题。首先是它没有向它的回调返回正确的参数,其次它并不总是使用它的回调。第二个问题是 huge ,因为它会导致程序挂起并中断。

我在功能中评论了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有一个很好的功能。如果您的瀑布步骤只是调用一个返回期望结果的函数,您可以使用async.apply进一步简化它。

第2次简化瀑布:

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 );
});

答案 1 :(得分: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 );
    });