Javascript Node.js是否使用以前的值进行循环?

Javascript Node.js是否使用以前的值进行循环?,javascript,node.js,Javascript,Node.js,目前,我正在尝试获取数组中每个值的md5。本质上,我在每个值上循环,然后散列它,就像这样 var crypto = require('crypto'); function userHash(userIDstring) { return crypto.createHash('md5').update(userIDstring).digest('hex'); } for (var userID in watching) { refPromises.push(admin.

目前,我正在尝试获取数组中每个值的md5。本质上,我在每个值上循环,然后散列它,就像这样

var crypto = require('crypto');
  function userHash(userIDstring) {
    return crypto.createHash('md5').update(userIDstring).digest('hex');
  }

  for (var userID in watching) {
    refPromises.push(admin.database().ref('notifications/'+ userID).once('value', (snapshot) => {
        if (snapshot.exists()) {
        const userHashString =  userHash(userID)
        console.log(userHashString.toUpperCase() + "this is the hashed string")
        if (userHashString.toUpperCase() === poster){
            return console.log("this is the poster")
        }
        else {
            ..
        }
    }
    else {
        return null
    }
      })
    )}
然而,这导致了两个问题。首先,我收到错误警告“不要在循环中生成函数”。第二个问题是,哈希值都返回相同的值。尽管每个
userID
都是唯一的,但userHashString在控制台日志中为每个用户打印出相同的值,就好像它只是在使用第一个userID,获取它的哈希值,然后每次都打印出来一样

最新更新:

exports.sendNotificationForPost = functions.firestore
    .document('posts/{posts}').onCreate((snap, context) => {
      const value = snap.data()
      const watching = value.watchedBy
      const poster = value.poster
      const postContentNotification = value.post
      const refPromises = []
      var crypto = require('crypto');
      function userHash(userIDstring) {
        return crypto.createHash('md5').update(userIDstring).digest('hex');
      }

      for (let userID in watching) {
        refPromises.push(admin.database().ref('notifications/'+ userID).once('value', (snapshot) => {
            if (snapshot.exists()) {
            const userHashString =  userHash(userID)

            if (userHashString.toUpperCase() === poster){
                return null
            }
            else {

                const payload = {
                    notification: {
                        title: "Someone posted something!",
                        body: postContentNotification,
                        sound: 'default'
                    }
                };

                return admin.messaging().sendToDevice(snapshot.val(), payload)
            }
        }
        else {
            return null
        }
          })
        )}
        return Promise.all(refPromises);
    });

这不是两个问题:你得到的警告是试图帮助你解决你注意到的第二个问题

问题是:在Javascript中,只有函数创建单独的作用域——在循环中定义的每个函数——使用相同的作用域。这意味着它们没有得到自己的相关循环变量的副本,它们共享一个引用(当第一个承诺得到解决时,它将等于数组的最后一个元素)


只需将的替换为。首先,在循环中有一个非阻塞异步操作。你需要完全理解这意味着什么。您的循环运行到结束,开始一系列非阻塞的异步操作。然后,当循环完成时,异步操作一个接一个地完成。这就是为什么循环变量
userID
的值不正确。当调用所有异步回调时,它位于终端值上

您可以在这里看到关于循环变量问题的讨论,其中有几个选项用于解决该问题:

其次,您还需要一种方法来知道所有异步操作何时完成。这有点像你发送了20只信鸽,却不知道它们什么时候都会给你带回一些信息(以任意顺序),所以你需要一种方法来知道它们什么时候都回来了

要知道所有异步操作何时完成,有许多不同的方法。“现代设计”和Javascript语言的未来将是使用承诺来表示异步操作,并使用
Promise.all()
来跟踪它们,保持结果有序,在它们全部完成时通知您,并传播可能发生的任何错误


以下是您的代码的清理版本:

const crypto = require('crypto');

exports.sendNotificationForPost = functions.firestore.document('posts/{posts}').onCreate((snap, context) => {
    const value = snap.data();
    const watching = value.watchedBy;
    const poster = value.poster;
    const postContentNotification = value.post;

    function userHash(userIDstring) {
        return crypto.createHash('md5').update(userIDstring).digest('hex');
    }

    return Promise.all(Object.keys(watching).map(userID => {
        return admin.database().ref('notifications/' + userID).once('value').then(snapshot => {
            if (snapshot.exists()) {
                const userHashString = userHash(userID);

                if (userHashString.toUpperCase() === poster) {
                    // user is same as poster, don't send to them
                    return {response: null, user: userID, poster: true};
                } else {
                    const payload = {
                        notification: {
                            title: "Someone posted something!",
                            body: postContentNotification,
                            sound: 'default'
                        }
                    };
                    return admin.messaging().sendToDevice(snapshot.val(), payload).then(response => {
                        return {response, user: userID};
                    }).catch(err => {
                        console.log("err in sendToDevice", err);
                        // if you want further processing to stop if there's a sendToDevice error, then
                        // uncomment the throw err line and remove the lines after it.  
                        // Otherwise, the error is logged and returned, but then ignored 
                        // so other processing continues

                        // throw err

                        // when return value is an object with err property, caller can see
                        // that that particular sendToDevice failed, can see the userID and the error
                        return {err, user: userID};    
                    });
                }
            } else {
                return {response: null, user: userID};
            }
        });
    }));
});
变化:

  • require()
    移出循环。没有理由多次打电话给它
  • 使用
    .map()
    收集
    Promise.all()
    的承诺数组
  • 使用
    Object.keys()
    从对象键获取一个用户ID数组,这样我们就可以对其使用
    .map()
  • .then()
    一起使用
  • 日志
    sendToDevice()
    错误
  • 使用
    Promise.all()
    跟踪所有承诺何时完成
  • 确保所有promise返回路径都返回一个具有一些公共属性的对象,以便调用者能够全面了解每个用户的情况

  • 我把它改成了forEach,除了refpromises.push实际上是在等待信鸽吗?因为它等待承诺,正确吗?@RaimKhalil-
    forEach()
    将解决您的循环迭代器问题,因为它为循环的每次调用生成唯一的函数调用和函数参数。
    admin.database().ref('notifications/'+userID).once('value',(snapshot)=>{})返回什么?它是否回报了承诺?通常,在同时支持回调和承诺的现代接口中,如果您像现在这样传递回调,它也不会返回承诺。对不起,我没有包括承诺的返回。这样做正确吗?(正如我更新的回答中所说)很抱歉我的困惑,我对node.js非常陌生。我不太确定它返回什么——在另一个问题中,有人回答说,
    你们都在向once()传递回调,并使用它返回的承诺。你应该选择其中一个,最好只是使用它的承诺。
    @RaimKhalil-
    返回承诺。全部(refPromises)refPromises
    实际上是一个承诺数组,那么code>是正确的想法,但我不认为这是因为您正在传递回调。我将在我的答案中添加一些内容,以便稍后向您展示。然后调用者必须对调用它的返回值使用
    .Then()
    。监视变量的
    数据类型是什么。它是一个用户ID数组吗?或者是一个以userID作为属性或值的对象?它是一个userID数组,那么
    for(var userID in watching)
    也不会迭代数组。迭代对象上的属性,而不是数组元素。切勿使用
    for/in
    迭代数组元素。从不在modern node.js中,使用
    for/of
    。如果您使用的是较新版本的node,您应该能够使用
    let
    而不是
    for
    循环中的
    var
    。@MarkMeyer.forEach对对象不起作用,是吗?那么用let替换var就可以解决这个问题了?