【问题标题】:Why can't Node see an element in an array when it is chosen by variable?为什么Node在变量选择时看不到数组中的元素?
【发布时间】:2016-09-27 12:24:30
【问题描述】:

在我正在编写的脚本中,我正在遍历邮箱中的一些邮件并尝试删除已阅读的邮件。我遇到的问题是,当我遍历数组时,Node 说它无法在给定元素处找到属性 id(这是删除或查看任何内容所必需的)。如果该属性不存在,这会很好,但是当使用 util.inspect 并指定 1 而不是迭代器变量时,它可以找到并打印该字段。现有代码如下所示:

             for(var b = 1; b < length; b++){
                setTimeout(function(){
                  console.log(util.inspect(messageList, false, null));
                  console.log(util.inspect(messageList[1], false, null));
                  console.log(util.inspect(messageList[1].id, false, null));
                  console.log(util.inspect(messageList[b].id, false, null));
                  var delID = messageList[b].id;
                  //console.log(util.inspect(messageList, false, null));
                  if(delID){
                    gmail.users.messages.trash({
                      auth:auth,
                      userId: 'me',
                      id: delID,
                    }, function(err2, delObject){
                      if(err2){
                        console.log("The message with ID: "+ delID + " could not be deleted");
                        console.log(err2);
                        return;
                      }
                      else{
                        console.log(util.inspect(delObject, false, null));
                        console.log("Message was deleted with id: " + delID);
                      }
                    })
                  }
                }, 2000);
              }

上面的 messageList 有一个实际数字而不是变量的行显示“157550983b6c1cb”(撇号也会打印出来)。在我用 b 切换它的那一行,它抛出:

    TypeError: Cannot read property of undefined
       at Timeout.oneTimeout(.......etc)
       at tryOnTimeout(timers.js:232:11)
       at Time.listonTimeout(timers.js:202:5)

这是否意味着无法在 Timeout 函数中使用变量 b?如果是这样,我如何绕过需要遍历整个 messageList 并在删除之间中断?

【问题讨论】:

  • b 和 messageList 值是什么?
  • b 介于 1 和数组长度之间,应该为每个数字调用该函数。但是它在第一个循环中抛出了一个错误,所以它在中断之前只有 1 个。 messageList 是一个包含两个字段的对象列表:id 和 threadId,可以使用 util.inspect 查看
  • Id idmessageList 中不可用,您尝试访问它会出错
  • 是的,我知道这就是错误的意思。我遇到的问题是,在上述 4 个控制台日志中的 3 个中,我可以看到我想要的。在最后一个我有“b”而不是数字 1 的地方,它表示该对象不存在。当指定 1 时,它打印出 id 就好了。
  • 你能在那个控制台前检查你的b 值吗?

标签: javascript arrays node.js object timeout


【解决方案1】:

因为 setTimeout 是异步的,所以在 for 循环完成之前不会调用回调 - 此时,b 将是 messageList.length - 太大了

您希望在每次迭代之间等待 2 秒 - 一种方法是使用这样的递归函数调用

var length = messageList.length;
function myFunction(b) {
    setTimeout(function() {
        console.log(util.inspect(messageList, false, null));
        console.log(util.inspect(messageList[1], false, null));
        console.log(util.inspect(messageList[1].id, false, null));
        console.log(util.inspect(messageList[b].id, false, null));
        var delID = messageList[b].id;
        //console.log(util.inspect(messageList, false, null));
        if (delID) {
            gmail.users.messages.trash({
                auth: auth,
                userId: 'me',
                id: delID,
            }, function(err2, delObject) {
                if (err2) {
                    console.log("The message with ID: " + delID + " could not be deleted");
                    console.log(err2);
                    return;
                } else {
                    console.log(util.inspect(delObject, false, null));
                    console.log("Message was deleted with id: " + delID);
                }
            })
        }
        b += 1;
        if (b < length) {
            myFunction(b);
        }
    }, 2000);
}
if (messageList.length > 0) {
    myFunction(0);
}
// note - any code here will run immediately! 
// it wont wait for the "iteration" to complete - 
// if you need to wait for the above to finish, 
// that's a whole other set of problems

根据 cmets,如果您希望第一次迭代立即运行,请将代码更改为

var length = messageList.length;
function myFunction(b) {
    console.log(util.inspect(messageList, false, null));
    console.log(util.inspect(messageList[1], false, null));
    console.log(util.inspect(messageList[1].id, false, null));
    console.log(util.inspect(messageList[b].id, false, null));
    var delID = messageList[b].id;
    //console.log(util.inspect(messageList, false, null));
    if (delID) {
        gmail.users.messages.trash({
            auth: auth,
            userId: 'me',
            id: delID,
        }, function(err2, delObject) {
            if (err2) {
                console.log("The message with ID: " + delID + " could not be deleted");
                console.log(err2);
                return;
            } else {
                console.log(util.inspect(delObject, false, null));
                console.log("Message was deleted with id: " + delID);
            }
        })
    }
    if (b < length - 1) {
        setTimeout(myFunction, 2000, b + 1);
    }
}
if (messageList.length > 0) {
    myFunction(0);
}
// note - any code here will run immediately! 
// it wont wait for the "iteration" to complete - 
// if you need to wait for the above to finish, 
// that's a whole other set of problems

如果您想让代码在迭代完成时运行,使用 Promises 可以让事情变得更简单 - 无论您是否有在完成时运行的代码,您都可以使用此方法

messageList
.slice(1) // skip the first item in the array as per question code - remove this line to start at 0
.reduce(function(prom, messageItem, index) {
    return prom
    .then(function () {
        if (index) { // this avoids the 2 second wait on the first item
            return new Promise(function(resolve) {
                setTimeout(resolve, 2000);
            });
        }
    })
    .then(function() {
        console.log(util.inspect(messageList, false, null));
        console.log(util.inspect(messageItem, false, null));
        console.log(util.inspect(messageItem.id, false, null));
        var delID = messageItem.id;
        //console.log(util.inspect(messageList, false, null));
        if (delID) {
            gmail.users.messages.trash({
                auth: auth,
                userId: 'me',
                id: delID,
            }, function(err2, delObject) {
                if (err2) {
                    console.log("The message with ID: " + delID + " could not be deleted");
                    console.log(err2);
                    return;
                } else {
                    console.log(util.inspect(delObject, false, null));
                    console.log("Message was deleted with id: " + delID);
                }
            });
        }
    });
}, Promise.resolve()) // this starts the promise chain
.then(function() {
    // this code gets called once the "loop" finishes
});
// code here does not wait for the "loop" to finish
// in fact it runs before the first messageList item is even processed

这段代码NOT在开始等待下一次迭代之前等待gmail.users.messages.trash完成 - 如果你需要等待,那么,这又是一个完全不同的问题,但非常使用 promise 方法时很容易处理

【讨论】:

  • 你是对的,它应该是 0,但我用随机数测试并把它留在了 1
  • 我现在正在测试它(整个脚本需要一些时间才能运行),但我知道以供将来参考,为什么会有从 b 开始的 setTimeout 调用(从 0 开始)以及超时调用下方的 if 语句?这会迫使它尝试删除每封电子邮件两次吗?
  • why is there the setTimeout call that would start at b (which would be 0 to start) - 因为这就是您的代码所做的 - 您是否希望第一次迭代没有延迟? - if 语句只继续进行下一次迭代 if b &lt; length - 注意 b += 1 - 与 b++ 相同 - 它递增 b
  • 我的意思是函数中有两个递归调用。一个在超时范围内,一个在超时范围内。看来是因为一个在 b 小于总长度时被调用,另一个在长度大于 0 时被调用,它们都有可能在相同的 b 值上被调用?这不正确吗?
  • 不,没有——对 myFunction(0) 的调用在 myFunction 之外,开始滚动
【解决方案2】:

这主要是因为 setTimeout 是异步的。

你可以试试这样的:

for(var a = 1; a < 10; a++) {
    (function(){
        var currentI = a;
        setTimeout(function(){
                console.log('Hi: sec: ', currentI);
        }, 1000);
    }());
}

在内部使用变量,将帮助您保持其状态 您可以查看this

【讨论】:

  • 问题已经发生了很大变化,这将不是一个有用的答案
猜你喜欢
  • 2012-02-13
  • 1970-01-01
  • 2020-11-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-20
相关资源
最近更新 更多