【问题标题】:Array returning Undefined because of asynchrony由于异步,数组返回未定义
【发布时间】:2016-05-24 00:12:54
【问题描述】:

我正在访问 API Trello,但遇到了以下问题:

Trello 访问信息,获取每个现有行的id,代码如下:

var x;  
var numberCardsByList = [];

trello.get("/1/boards/[idBoard]/lists/all", function(err, data) {
    if (err) throw err;
    console.log("Number of list: " + data.length);

    for(var i=0; i<data.length; i++){
        x = data[i];
        findNumberCards(x);
    }
});

如您所见,在获得大小后,我遍历所有这些队列,在循环中,将每一行附加到变量 x 中,并调用旨在获取队列中卡片数量的函数。卡片数量代码如下:

function findNumberCards(x){
    trello.get("/1/lists/"+x.id+"/cards", function(err, dados){
        if(err) throw err;
        console.log("Name List: " + x.name + " have " + dados.length + " cards");
        numberCardsByList[x.name] = dados.length;
    });
}

直到那时还好,但是当我在 Trello 中搜索结束后尝试访问向量 numberCardsByList 时,它返回 undefined:

var x;  
var numberCardsByList = [];

trello.get("/1/boards/[idBoard]/lists/all", function(err, data) {
    if (err) throw err;
    console.log("Quantidade de Filas: " + data.length);

    for(var i=0; i<data.length; i++){
        x = data[i];
        findNumberCards(x);
    }
});
console.log(numberCardsByList);

我知道是因为异步,但是无法解决。

【问题讨论】:

  • 请以更完整的方式展示您的代码示例。我认为您首先调用/1/boards/[idBoard]/lists/all,然后在回调中调用另一个执行异步操作的函数,然后如果我错了,你会做其他事情纠正我的功能
  • 是的,我正在解决这个问题?

标签: javascript node.js asynchronous


【解决方案1】:

您所面临的问题已经解决了很多次。如果您想了解更多信息,请搜索关键字“承诺”。如果您熟悉 jQuery,请尝试查找:$.whenAll$.ajax().done$.ajax().always 等。

如果你想自己想出一个轻量级的解决方案,这里有一个指针:

当您到达console.log(numberCardsByList) 时,由findNumberCards 触发的请求尚未完成,因此Array 为空。您需要确保知道所有 findNumberCards 请求何时完成并然后记录它们。或者,您可以在其中一个完成时记录它们

大致有两种方法:

  1. 跟踪您的打开请求并在处理请求时调用函数。
  2. 观察您的 numberCardsByList 对象并在添加项目时调用一个函数(您不知道它们是异步添加还是同步添加)

我建议采用第一种方法。查看此示例代码和 cmets:

var numberCardsByList = {};

// This array will store the url for every open request
var openRequests = [];

var removeRequest = function(url) {
  var index = openRequests.indexOf(url);
  if (index === -1) return;

  // Remove url from array
  openRequests = openRequests
    .slice(0, index)
    .concat(openRequests
      .slice(index + 1));
};

// This will be called whenever one request completes
var onComplete = function(url) {
  removeRequest(url);
  
  // When all have completed, we can call our callback
  if (openRequests.length === 0) {
    onAllComplete();
  }

});

// This will be called when there are no open requests left
var onAllComplete = function(data) {
  console.log(numberCardsByList);
}


trello.get("/1/boards/[idBoard]/lists/all", function(err, data) {
  if (err) throw err;
  console.log("Number of list: " + data.length);

  for (var i = 0; i < data.length; i++) {
    x = data[i];
    findNumberCards(x);
  }
});


function findNumberCards(x) {
  var url = "/1/lists/" + x.id + "/cards";
  
  // Before we make the request, we register it:
  openRequests.push(url);

  trello.get(url, function(err, dados) {
    numberCardsByList[x.name] = dados.length;
    
    // When it is completed, we call onComplete
    onComplete(url);
  });
};

请注意,此 onAllComplete 并非 100% 安全:如果一个请求在下一个请求开始之前完成,它可能会被多次调用。

结论:

如果可以,我会使用库来处理承诺。如果您想尝试自己构建一些东西,您可以尝试跟踪请求并在它们全部完成后执行回调。

【讨论】:

    【解决方案2】:

    请记住,我上面的代码很可能对您不起作用,因为我不知道您的代码中发生了什么,所以这是一个示例/说明如何处理您的问题。

    由于您不熟悉异步操作,我会假设您没有关于 Promise 的先验知识,因此会给您一个不太理想的解决方案 - 但是 Promise 更好,您应该明确地学习它们。

    您需要在异步代码的结果中执行序列过程。

    首先,您将为第二个操作创建一个函数,例如:

    function numberCardsByList (param1,param2){.....}
    

    然后您将更改 FineNumberCards 以也接受回调:

    function findNumberCards(x, callback){
        trello.get("/1/lists/"+x.id+"/cards", function(err, dados){
            if(err) throw err;
            console.log("Name List: " + x.name + " have " + dados.length + " cards");
            numberCardsByList[x.name] = dados.length;
        });
    
        // pass in any params you need.
        callback();
    }
    

    然后你会将新创建的函数numberCardsByList 传递给findNumberCards 或任何你想要的地方。 trello.get("/1/boards/[idBoard]/lists/all", function(err, data) { if (err) 抛出错误; console.log("列表个数:" + data.length);

        for(var i=0; i<data.length; i++){
            x = data[i];
            // and here we are calling findNumberCards and passing in the callback..
            findNumberCards(x, numberCardsByList);
        }
    });
    

    这通常是您处理异步操作的方式,您将传递一个回调以供下一个要执行的操作。

    更新

    这里有一个示例,说明如何在另一个场景中完成此操作,只是为了进一步说明这一点。

    我们从获取用户开始

    service.getUser(userName, function(err,user){
      if(user) {
        // we get user picture passing getPictureSize as callback
        getUserPicture(user.picture, getPictureSize)
      }
    })
    

    我们得到图片的URL

    function getUserPicture(picName, cb){
      service.getPictureURL(picName, function(err, pictureURL){
        if(pictureURL) {
          // we then call the callback - the next async operation we want.
          cb(pictureURL);
        }
      });
    }
    

    我们得到图片大小 - 这是最后一个操作

    function getPictureSize(pictureURL){
      service.getPictureSize(pictureURL, function(err, pictureSize){
        $('.picName').attr('src', picName);
        $('.picName').width(pictureSize.width);
        $('.picName').height(pictureSize.height);
      });
    
    }
    

    我希望能澄清一点。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-09-22
      • 2016-05-07
      • 1970-01-01
      • 1970-01-01
      • 2020-09-05
      • 2021-05-20
      • 1970-01-01
      相关资源
      最近更新 更多