【问题标题】:NodeJS Promise FirebaseNodeJS 承诺 Firebase
【发布时间】:2019-03-19 06:10:44
【问题描述】:

喜欢 nodeJS 和异步特性!有了这个,我很困惑如何继续,因为我无法保持嵌套承诺,当然这是不行的,所以我举起双手,因为每一步都需要使用上一步的数据完成操作。

这就是我想要完成的,代码如下。

  1. 一所新学院进入 /sessions/college
  2. 获得该密钥的值后,寻找订阅该学院的顾问。
  3. 为订阅顾问获取 FCM 代币
  4. 显然还没有进入这部分,但向订阅者发送 FCM 通知。
  5. 多田!

exports.newSessionNotifer = functions.database.ref('/sessions/college').onCreate((snap, context) => {
    const college = snap.val(); 
    var promises = [];
    var getAdvisors = admin.database().ref('colleges').child(college).once('value').then((snapshot) => {
        const people = snapshot.val();
        var advisors = Object.keys(people);
        return advisors;
    }).then((advisors) => {
        return advisors.forEach((token) => {
            var advisorToken = admin.database().ref('users').child(token).child('fcmtoken').child('token').once('value');
            return console.log(advisorToken);
        });
    });

    return Promise.all(promises).then((values) => {
        console.log(promises);
        return console.log('Hi');
    });

【问题讨论】:

  • 你打算用那个 promises 数组做什么?
  • Don't use forEach! 如果advisors 不是数组而是firebase 快照,请先将其转换为数组。
  • 顾问“可以”是一个数组,所以我想通过只做一个 forEach 来适应它成为一个数组的可能性。
  • 要点(如链接中所述)是您不能将forEach 与执行异步操作的回调一起使用。改用map 创建一个promise 数组。
  • I can't keep nesting promises - 事情是,你没有嵌套的承诺 - 如果你真的想要“扁平”代码......async/await 来救援

标签: node.js firebase firebase-realtime-database promise google-cloud-functions


【解决方案1】:

你在正确的轨道上。 once() 返回一个 Promise,它是来自重复调用的一组 Promise,必须收集并使用 Promise.all() 运行。

exports.newSessionNotifer = functions.database.ref('/sessions/college').onCreate((snap, context) => {
    const college = snap.val();
    return admin.database().ref('colleges').child(college).once('value');
}).then(snapshot => {
    const people = snapshot.val();
    let advisors = Object.keys(people);
    let promises = advisors.map(token => {
        return admin.database().ref('users').child(token).child('fcmtoken').child('token').once('value');
    });
    return Promise.all(promises);
});

EDIT 再次编辑,这次是 OP 的答案在手。关于样式,我不确定 lint 说什么,但我对不良嵌套样式的定义是 then() 块包含另一个 then() 块。同样关于风格,我让这些东西易于理解的方法是构建(和测试)小函数,每个异步任务一个。

在结构上,OP 的新答案不必要地在return advisors 之后链接了第二个块。由于顾问不是一个承诺,我们可以从那里继续使用同步代码。同样在结构上,OP 的解决方案创建了一系列承诺——每个顾问两个承诺(获取顾问令牌并推送)——但除非应用并返回 Promise.all,否则这些承诺不一定会完成。

综上所述,我的建议如下......

在创建时,获取学院的顾问,向每个人发送消息。

exports.newSessionNotifer = functions.database.ref('/sessions/{sessionID}/college').onCreate((snap, context) => {
    const college = snap.val(); 
    return advisorsForCollege(college).then(advisors => {
        let promises = advisors.map(advisor => sendAdvisorMessage(advisor, college));
        return Promise.all(promises);
    });
});

一所大学的顾问显然是该大学对象的钥匙

function advisorsForCollege(college) {
    return admin.database().ref('colleges').child(college).once('value').then(snapshot => Object.keys(snapshot.val()));
}

发送顾问消息意味着获取顾问令牌并进行推送。返回一个有两个承诺的链......

function sendAdvisorMessage(advisor, college) {
    return tokenForAdvisor(advisor).then(token => {
        let title = `There's a new session for ${college}!`;
        let body = 'Go to the middle tab and swipe right to accept the session before your peers do!'
        return sendToDevice(token, title, body);
    });
}

现在我们只需要一个来获取顾问的令牌,另一个来进行推送......

function tokenForAdvisor(advisor) {
    return admin.database().ref('users').child(advisor).child('fcmtoken').child('token').once('value');
}

function sendToDevice(token, title, body) {
    const payload = { notification: { title: title, body: body } };
    return admin.messaging().sendToDevice(token, payload);
};

我认为 lint 应该报告所有上述情况,即使打开了承诺嵌套警告。

【讨论】:

  • 我认为onCreate 不会返回承诺,是吗?
  • @Bergi:每个在关闭} 执行后需要继续运行的云函数都需要返回一个promise。否则,Cloud Functions 不知道该功能仍处于活动状态,并且可能会关闭/重用其容器。请参阅 onCreate 返回承诺的示例:firebase.google.com/docs/functions/…
  • @FrankvanPuffelen 好的,我仍然理解 OP 的代码,因此他希望他的承诺链位于 onCreate 回调中
  • 我希望 Promise 链位于 onCreate 回调中,因为这些操作专门与函数相关联。
  • @Ben,很抱歉我帮不上忙。我不明白你对嵌套的定义。也许将其添加到您对其他人的问题中。另外,你知道,不嵌套是一种风格,与其说是不应该去,不如说是不应该去。也许,对于任何不起作用的东西,也可以在你的 OP 中添加一些失败的证据。
【解决方案2】:

感谢 danh,这是我的最终代码。评论/反馈!我决定在 lint 和 viola 中禁用 promise 嵌套选项!

exports.newSessionNotifer = functions.database.ref('/sessions/{sessionID}/college').onCreate((snap, context) => {
const college = snap.val(); 
return admin.database().ref('colleges').child(college).once('value').then((snapshot) => {
    const people = snapshot.val();  
    let advisors = Object.keys(people);
    return advisors;
}).then((advisors) => {
    return advisors.map(advisor => {
            return admin.database().ref('users').child(advisor).child('fcmtoken').child('token').once('value').then((snapshot) => {
            const token = snapshot.val();
            const payload = {
                    notification: {
                    title: `There's a new session for ${college}!`,
                    body: 'Go to the middle tab and swipe right to accept the session before your peers do!'
                    }
            };
            return admin.messaging().sendToDevice(token, payload);
        });
    });
});
});

【讨论】:

  • 很高兴您找到了解决方案。我在另一个编辑中对我的答案做了几个样式和结构 cmets(不确定我应该在这里还是那里编辑)
  • 太棒了,要对其进行测试,但假设它会起作用,并且不可否认,我提到了将所有内容分解为函数的想法,但当然几乎不知道如何做到这一点再次感谢!
猜你喜欢
  • 2015-03-30
  • 2018-10-06
  • 2017-04-19
  • 1970-01-01
  • 2019-09-29
  • 1970-01-01
  • 1970-01-01
  • 2020-08-19
  • 1970-01-01
相关资源
最近更新 更多