【问题标题】:How do I deal with recursive callback in node?如何处理节点中的递归回调?
【发布时间】:2017-02-24 04:05:42
【问题描述】:

我正在使用Robinhood-node API 来检索我的安全投资组合。 API 有两个我使用的函数 -

orders(callbackfn)

检索最后 100 个订单和下一页的链接。

url(url,callback)

获取具有 100 多个订单的下一页。

这两个函数都返回一个类似于 -

的 JSON
{
    results:[
        {order 0},
        {order -1},
        ...
        {order -100}],
    next: url_for_next_page
}

现在,我需要将下一个 url 传递给 url 函数以获取另外 100 个订单。最后一页有next: null

在处理orders 的结果之前,无法调用url。在处理之前的url 结果之前,无法调用后续的 url。这导致我创建了一个回调地狱:-( order -> url -> url -> url ...

我不能循环调用url方法。循环在回调执行之前继续执行。

如何反复拨打url?到目前为止,我已经使用递归函数调用管理了一个 hack。这是我的代码 - https://gist.github.com/lordloh/f5c8c589b4538ab9674db919ae2e2834

我正在学习 node js... :-/

【问题讨论】:

  • 是的,递归方法是解决异步遍历的唯一方法。
  • 我不应该担心堆栈空间不足吗?特别是因为我不知道我可能需要进行多少次递归。随着时间的推移,我会有更多的交易,递归的次数也会增加。
  • @LordLoh.,这是一个很好的观点。我更新了我的答案以避免这个问题 - 如果你添加一个 setTimeout0 时间,该函数将被添加到事件队列中,并且没有机会耗尽堆栈空间。
  • @LordLoh。 No。您的函数是异步的,回调将使用新堆栈调用。

标签: javascript node.js recursion callback


【解决方案1】:

您是否考虑过使用Promises 来避免回调地狱?

我查看了您的代码,我想像这样使用Promises

const credentials = {
    username: 'username',
    password: 'Password'
};

const Robinhood = require('../robinhood-node/src/robinhood');

function orderHandler(orderArray,allOs){
    return new Promise((resolve, reject) => 
    {       
        for (let i = 0; i < orderArray.length; i++) {
            var e = orderArray[i];
            var ss = e.instrument.split('/');
            var instrument = ss[ss.length-2];
            allOs.push({'id':e.id,
              'instrument':instrument,
              'quantity':e.quantity,
              'price':e.average_price,
              'side':e.side,
              'transaction_time':e.last_transaction_at
            });
        }
        resolve(allOs);     
    });
}

Robinhood(credentials, () => 
{
  Robinhood.orders(function(e,r,b){   
    orderHandler(b.results,[]).then((allOs) => 
    {
        urlSequence(b.next, allOs).then((allOS) => csv(allOs));             
    });
  });       
});

function urlSequence(url,allOs){
    if(!url)
        return Promise.resolve(allOS);

    return new Promise((resolve, reject) =>
    {
        Robinhood.url(url, (e,r,b) => 
        {
            orderHandler(b.results, allOS)
                .then((allOs) => {
                    setTimeout(() => resolve(urlSequence(b.next, allOs)), 0);                       
                });     
        });     
    });
}

function csv(Arr){
    var A;
    for (var i=0;i<Arr.length;i++){
        A=Arr[i];
        console.log(A.id+", "+A.instrument+", "+A.quantity+", "+A.price+", "+A.quantity+", "+A.side+", "+A.transaction_time);
    }
}

【讨论】:

    【解决方案2】:

    假设我们有一个名为 fetch 的函数,它所做的只是获取一些数据并将其传递给回调,这就是它的工作方式:

    function getAllPages(url, callback) {
      // This is a function that returns a function:
      const getPage = (oldData) => (response) => {
        // We concat the data from the response to our collected list:
        const newData = oldData.concat(response.results)
    
        if(response.next && response.next.length) {
          // Call the function returned by getPage passing in newData
          // This is what we call recursion
          fetch(response.next, getPage(newData))
        } else {
          // If there is no next page we just pass all the collected data to a callback:
          callback(newData)
        }
      }
    
      // We pass it an empty list as initial data:
      getPage([])({next: url, results: []})
    }
    
    getAllPages('some url...', (data) => {
      console.log(data)
    })
    

    我希望这会帮助你继续前进。

    如果页面过多,您可以使用setTimeout 轻松添加一些限制或设置限制。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-06-06
      • 1970-01-01
      • 1970-01-01
      • 2014-05-23
      • 2019-02-12
      • 2011-12-24
      • 2012-08-02
      • 2020-09-09
      相关资源
      最近更新 更多