在致电Firebase Cloud Messaging sendToDevice时,程序不会退出

时间:2017-12-12 10:49:32

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

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

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
};

database.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
};

最后,发件人.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发送到promise链,但没有成功......

所以......我哪里错了? 非常感谢任何可以帮助我解决这个错误的人。

修改:

我在@ {JimWright问的.then()中的抓取之前添加了builder.js

结果如下:

结果如下:

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

2 个答案:

答案 0 :(得分:1)

你应该抛出返回的错误。

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
    });
}

答案 1 :(得分:0)

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

在你的主要功能中,你应该做这样的事情(我没有找到你对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