【问题标题】:Program won't exit when call to Firebase Cloud Messaging sendToDevice调用 Firebase Cloud Messaging sendToDevice 时程序不会退出
【发布时间】:2018-05-26 00:41:03
【问题描述】:

我尝试在 node.js 中创建一个通知服务器,它从数据库中获取通知,编辑其有效负载,通过 Firebase 云消息传递发送它们,然后在数据库中编辑它们的状态。

Javascript 不是我的主要语言,所以我希望他们在这段代码中没有太多的误解。

为此,我使用了一些 Promises 和一个 Promise.all。

目前,问题是当我调用 firebase.admin.messaging().sendToDevice 时,我的应用程序永远不会结束执行。

代码如下:

main.js:

'use strict';
global.basePath = __dirname + '/';

  const  conf   = require('./config/config'),
         db     = require('./lib/database'),
         builder = require('./notify/builder');

const gender  = conf.genderId[process.argv[2]],
      type    = conf.typeId[process.argv[3]],
      confSql = conf.inc.prod ? conf.mysql.prod : conf.mysql.dev,
      database = new db.database(confSql);

const notify = new Promise(
    (resolve, reject) => {
        if (typeof(gender) !== 'number' && typeof(type) !== 'number') {
            return reject('Error: gender and type are mandatory');
        }
        resolve();
    }
);


function main () {

    notify
    //Get the notifications from DB - They are already group by user
    .then( () => { return database.getNotifications(gender, type); })
    //Set the payload, send to Firebase, and update the status in DB
    // <-- Inside it is the call to Firebase
    .then( rows => { return Promise.all(rows.map(builder.handleNotification)); } 
        , err => {
            return database.close().then( () => {
                return Promise.reject(err)
            } );
        }
    )
    .then(() => {
        console.log('Success ! The DB and the app must close.');
        database.close();
    })
    .catch(console.log.bind(console))
    ;
}


main();

builder.js:

'use strict';
const conf = require('./../config/config'),
      sender = require('./sender'),
      database = require('./../lib/database');


//This is called inside an array.map
//It is a chain of Promises that are resolved or rejected in a Promise.all
function handleNotification( notification){  
    let notif = notification;

    return Promise.resolve(setGroupPayload(notification))
        .then(sender.send)
        .then(console.log)
        .catch(error => {
            return Promise.reject(error);
        });
}


function setGroupPayload (notification){

    //Do some change on notifications
    // (...)

    return notification;
}

module.exports = {
    handleNotification: handleNotification
};

数据库.js:

const mysql = require( 'mysql' );



function Database(config) {
    this.connection = mysql.createConnection( config );
}

Database.prototype.query = function query( sql, args ) {
    return new Promise( ( resolve, reject ) => {
        this.connection.query( sql, args, ( err, rows ) => {
            if ( err )
                return reject( err );
            resolve( rows );
        } );
    } );
};


Database.prototype.ping = function ping(){
    return new Promise( ( resolve, reject) => {
        this.connection.ping( err => {
            if ( err )
                return reject( err );
            resolve('Server responded to ping');
        } );
    } );
};

Database.prototype.close = function close() {
    console.log('close connection');
    return new Promise( ( resolve, reject ) => {
        this.connection.end( err => {
            if ( err )
                return reject( err );
            console.log('connection closed');
            resolve();
        } );
    } );
};


Database.prototype.getNotifications = function getNotifications (gender, type) {
    const query = `(...)`;
    const params = [gender, type];

    return this.query(query, params);
};

module.exports = {
    database: Database
};

最后是 sender.js :

'use strict';
const firebase = require('./../lib/firebase-admin');

/**
 *
 * @param notification
 * @returns {Promise}
 */
function send (notification) {

    if (notification.message === false) {
        return Promise.reject(["payload is empty"]);
    }
    if (!(notification.token && notification.token.length > 0)) {
        return Promise.reject(["target is empty."]);
    }

    const options = {
        contentAvailable: true
    };

    //When this is called here, the app never ends
    return firebase.admin.messaging().sendToDevice(notification.token, notification.message, options);  /
}


module.exports = {
    send: send
};

我收到了来自firebase.admin.messaging().sendToDevice(notification.token, notification.message, options) 的以下回复,即Promise.resolve

[ { error: { [Error: The provided registration token is not registered. A previously valid registration token can be unregistered for a variety of reasons. See the error documentation for more details. Remove this registration token and stop using it to send messages.] errorInfo: [Object], codePrefix: 'messaging' } } ]

这是正确的,因为令牌无效。我想处理这个响应。但我不明白,为什么我的应用程序永远不会结束?看起来他们是 Promise.all 中一个永无止境的承诺,阻止应用程序结束。

我也尝试处理来自 Firebase 的响应并将 Promise.reject 发送到承诺链,但没有成功...

那么...我哪里错了? 非常感谢任何可以帮助我解决此错误的人。

编辑:

按照@JimWright 的要求,我在builder.js 的catch 之前添加了.then()

结果如下:

结果如下:

{ results: [ { error: [Object] } ],
  canonicalRegistrationTokenCount: 0,
  failureCount: 1,
  successCount: 0,
  multicastId: 6057186106180078000 }
Success ! The DB and the app must close.
close connection
connection closed

【问题讨论】:

  • 尝试在builder.js 的捕获之前添加then() - 你在那里得到结果吗?还要记录错误以查看是否有任何内容。
  • 是来自then() 还是catch()
  • 感谢您的回答@JimWright。我已经编辑了帖子。 sendToDevice 发送一个 Promise.resolve()。所以即使它发送一个错误,它仍然是一个解析。

标签: javascript node.js firebase promise firebase-cloud-messaging


【解决方案1】:

您在使用 firebase admin 后是否正在调用 app.delete() 函数?必须调用它来关闭连接和后台任务。

在你的 main 函数中你应该做这样的事情(我没有发现你对 firebase.initializeApp() 的调用是所以我假设它在 main.js 文件中):

const firebase = require('firebase-admin');
const firebaseApp = FirebaseAdmin.initializeApp()

function main () {
    notify
    //Get the notifications from DB - They are already group by user
    .then( () => { return database.getNotifications(gender, type); })
    //Set the payload, send to Firebase, and update the status in DB
    // <-- Inside it is the call to Firebase
    .then( rows => { return Promise.all(rows.map(builder.handleNotification)); } 
        , err => {
            return database.close().then( () => {
                return Promise.reject(err)
            } );
        }
    )
    .then(() => {
        console.log('Success ! The DB and the app must close.');
        database.close();
        firebaseApp.delete(); // Add this to finish firebase background tasks
    })
    .catch(console.log.bind(console))
    ;
}

参考文献:

How to properly exit firebase-admin nodejs script when all transaction is completed

https://github.com/firebase/firebase-admin-node/issues/91

【讨论】:

    【解决方案2】:

    您应该抛出返回的错误。

     sender.js

    function send (notification) {
    
        if (notification.message === false) {
            return Promise.reject(new Error("payload is empty"));
        }
        if (!(notification.token && notification.token.length > 0)) {
            return Promise.reject(new Error("target is empty."));
        }
    
        const options = {
            contentAvailable: true
        };
    
        //When this is called here, the app never ends
        const response = firebase.admin.messaging().sendToDevice(notification.token, notification.message, options);
        if ('error' in response[0] and response[0]['error']) {
            return Promise.reject(response[0]['error']);
        );
        
        return response;
    }
    

    编辑:

    从日志看来,您的代码正在执行到最后一点。您应该使用.finally() 关闭所有连接,因为无论承诺是否被解决或拒绝,此关闭都会运行。

    main.js

    function main () {
    
        notify
        //Get the notifications from DB - They are already group by user
        .then( () => { return database.getNotifications(gender, type); })
        //Set the payload, send to Firebase, and update the status in DB
        // <-- Inside it is the call to Firebase
        .then( rows => { return Promise.all(rows.map(builder.handleNotification)); } 
            , err => {
                return database.close().then( () => {
                    return Promise.reject(err)
                } );
            }
        )
        .then(() => {
            console.log('Success!');
            // database.close();
        })
        .catch(console.log.bind(console))
        .finally(() => {
            console.log('Closing all connections...');
            database.close();
            console.log('All connections closed.');
            // Execution should stop here
        });
    }
    

    【讨论】:

    • 永远不要拒绝 JavaScript 中的非 Error 对象,这将消除您的生产堆栈跟踪。
    • 我只添加了最后几行 - 我相信评论是给 OP 的。
    • response 只是一个 Pending Promise,因此它必须调用 .then() 才能访问 results 和 error 属性。
    • 是的,对不起@JimWright 我错过了它在原始帖子中,然后无法取消投票。我已经做了。我建议今天不要使用 bluebird 或 Q(我是两者的维护者),除非您有令人信服的理由 - 我们一直在努力为 Node 和浏览器中的原生 Promise 添加调试功能。
    • 随时在 JavaScript 聊天室中联系我 chat.stackoverflow.com/rooms/17/javascript 并联系我 @BenjaminGruenbaum
    猜你喜欢
    • 1970-01-01
    • 2020-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-22
    • 2021-01-28
    • 2017-02-25
    • 2023-04-01
    相关资源
    最近更新 更多