使用以前的值进行Node.js循环吗?

时间:2018-08-04 22:36:24

标签: 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.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);
    });

2 个答案:

答案 0 :(得分:1)

这些不是两个问题:您得到的警告正在试图帮助您解决发现的第二个问题。

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

只需将for替换为.forEach

答案 1 :(得分:1)

您在这里遇到了几个问题。首先,您在循环内有一个非阻塞的异步操作。您需要完全理解这意味着什么。您的循环运行完成,开始了一系列非阻塞的异步操作。然后,当循环完成时,您的异步操作将一一完成。这就是为什么循环变量userID位于错误的值的原因。当所有异步回调都被调用时,它在终端值上。

您可以在此处看到有关循环变量问题的讨论,其中包含一些用于解决该问题的选项:

Asynchronous Process inside a javascript for loop

第二,您还需要一种方法来知道何时完成所有异步操作。就像您派出了20羽信鸽,不知道它们何时会带回您一些消息(以任意顺序),因此您需要一种方法来知道它们何时都回来了。

要知道何时完成所有异步操作,可以使用多种方法。 “现代设计”和Java语言的未来将是使用Promise来代表您的异步操作,并使用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};
            }
        });
    }));
});

更改:

  1. require()移出循环。没有理由多次调用它。
  2. 使用.map()来收集Promise.all()的诺言数组。
  3. 使用Object.keys()从对象键中获取一个用户ID数组,这样我们就可以在其上使用.map()
  4. .then().once()一起使用。
  5. 记录sendToDevice()错误。
  6. 使用Promise.all()跟踪所有承诺的完成时间
  7. 确保所有的promise返回路径都返回具有一些公共属性的对象,以便调用者可以全面了解每个用户的情况