我们如何在节点js中使用promises?

时间:2016-04-07 06:01:01

标签: javascript node.js promise

在异步编程中,我们习惯于回调和承诺。 在这里,我陷入了可能用于承诺的问题。我谷歌很多,但没有找到解决我的问题。

这是我在Android设备中发送推送通知的代码。

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

输出:

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

我认为它应该是这样的。

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

2 个答案:

答案 0 :(得分:2)

您可以使用async.mapSeries方法来链接通知。将for循环替换为:

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

答案 1 :(得分:2)

我建议使用Bluebird JS进行Promise流量控制。

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

});

注意:实际上或多或少没有任何库但是使用本机Promise,但语法会更混乱。