iam
iam

Reputation: 1013

How we can use promises in node js?

As in asynchronous programming we used to callbacks and promises. Here I am stuck in a problem that may be used to promises. I google it a lot but there is nothing found that solved my problem.

Here My code that I am doing to send push notification in android device.

router.post('/check-notifications', function(req, res, next) {

    var user_id = req.body.user_id;

    var response = {};
    var gcm = require('push-notify').gcm({

        apiKey: gcm_apiKey,
        retries: 0
    });

    connection.query('select device_id from devices where user_id = '+ user_id, function (err, result) {
        if ( result.length ) {

            for (var i = 0; i < result.length; i++) {

                console.log(i + 'before notify');
                gcm.send({

                    registrationId: result[i]['device_id'],
                    data: result[0]
                });

                console.log(i + 'before transmitted');
                gcm.on('transmitted', function (result, message, registrationId) {
                    console.log('transmitted');
                });

                gcm.on('transmissionError', function (error, message, registrationId) {
                    console.log(message);
                });

                console.log(i + 'after notify');

            }
        }           
    });

    response['success'] = true;
    response['msg'] = 'sent successfully';
    res.json(response);
}); 

Output :

0before notify
0before transmitted
0after notify
1before notify
1before transmitted
1after notify
transmitted
transmitted
transmitted
transmitted

And I think It should be like this.

0before notify
0before transmitted
transmitted
0after notify
1before notify
1before transmitted
transmitted
1after notify

Upvotes: 0

Views: 140

Answers (2)

kazenorin
kazenorin

Reputation: 1465

I recommend using Bluebird JS for Promise flow-control.

var Promise = require('bluebird'); // Require bluebird, and call it 'Promise', the code below is version 3.x syntax

var connection = {'query': '???'}; // assuming `connection` is already defined somewhere else
var gcm_apiKey = '???'; // assuming `gcm_apiKey` is already defined

router.post('/check-notifications', function (req, res, next) {

    var user_id = req.body.user_id;

    var gcm = require('push-notify').gcm({
        apiKey: gcm_apiKey,
        retries: 0
    });

    // assuming `connection` is already defined somewhere else
    // Make an async version of connection.query
    connection.queryAsync = Promise.promisify(connection.query);

    connection.queryAsync('select device_id from devices where user_id = ' + user_id)
        // Bluebird's Promise.map would execute the following block once per result, asynchronously.
        // The sequence of who runs first and who completes first is undefined
        .map(function (result, i) {
            // the `result` argument here is `result[i]` of the original code, since we're in the map context

            // Here we have to create a promise to consume events
            return new Promise(function (resolve, reject) {

                console.log(i + 'before notify');
                gcm.send({
                    registrationId: result['device_id'],
                    data: result // original code is written as result[0], which I don't quite understand. Always sending the first result?
                });

                // This does not make sense console logging here, as it is not actually 'before transmitted'
                // It's just binding onto the event
                // console.log(i + 'before transmitted'); 
                gcm.on('transmitted', function (result, message, registrationId) {
                    // Check registrationId
                    if (registrationId === result['device_id']) {
                      console.log('transmitted');
                      resolve(result); // use 'result' as the Promise's resolved value 
                    }
                });

                gcm.on('transmissionError', function (error, message, registrationId) {
                    // Check registrationId
                    if (registrationId === result['device_id']) {
                      console.log(message);
                      reject(message); // reject errors and send the message as the promise's reject reason
                    }
                });

                // Technically, you should log it as "after event binding"
                console.log(i + 'after notify');

            });
        }).then(function (results) {
            // `results` should contain all the result from the 'transmitted' event

            var response = {};
            response['success'] = true;
            response['msg'] = 'sent successfully';
            res.json(response);
        });

});

Note: The is actually more or less doable without any libraries but with native Promises, but the syntax would be more cluttering.

Upvotes: 2

Dima Fitiskin
Dima Fitiskin

Reputation: 323

You can use async.mapSeries method for chaining notifications. Replace for loop to:

async.mapSeries(result, function(item, callback) {
    gcm.send({
        registrationId: item['device_id'],
        data: data
    });
    gcm.on('transmitted', function(result, message, registrationId) {
        console.log('transmitted');
        callback(null, message, registrationId);
    });

    gcm.on('transmissionError', function(error, message, registrationId) {
        callback(error, message, registrationId);
    });
}, function (err, results) {
    if (err) throw err;
    response['success'] = true;
    response['msg'] = 'sent successfully';
    res.json(response);
})

Upvotes: 2

Related Questions