Search code examples
javascriptmysqlnode.jspromisebluebird

NodeJS Promises can't edit existing 'model'


Edit Upon closer inspection, it would appear that async.forEach finishes before getCurrencies is even run the first time... How do I resolve this? I thought returning a promise would essentially 'chain' these promises together?

I have the below code, which attempts to read transactions from mysql. Due to the size of the table and the length of time query takes, I'm breaking it up and using promises to populate an object. However, getCurrencies doesn't update the model's currencyCode - Yet, both console.log report correct values. Can someone explain what I'm doing wrong please? I'm quite new to promises still. Thanks.

main object building

    connection.query(query).then(function(items) {
        async.forEach(items, function(element) {
            element.ptype = 'merchants';
            element.type = 'transactions';
            element.currencyCode = null;

            getCurrencies(element.currencies_id).then(function(result) {
                console.log(result.currencyCode);
                element.currencyCode = result.currencyCode;
                console.log(JSON.stringify(element));
            });
        });

        res.send(items);
    });

getCurrencies

function getCurrencies(transactionId) {
    var currencies_query = '';
    currencies_query += 'SELECT ';
    currencies_query += 'iso_code AS currencyCode, ';
    currencies_query += 'name AS `currencyName`, ';
    currencies_query += 'symbol AS `currencySymbol`, ';
    currencies_query += 'exponent AS `currencyExponent`, ';
    currencies_query += 'iso_num AS `currencyNumber` ';
    currencies_query += 'FROM ';
    currencies_query += 'currencies ';
    currencies_query += 'WHERE ';
    currencies_query += 'id = ' + transactionId;

    return new Promise(function(resolve, reject) {
        connection.query(currencies_query).then(function(rows) {
            resolve(rows[0]);
        });
    });
};

And output from both console.logs

first

GBP

second

{"id":11213595,"pid":100856,"uri":"/rest/transactions/11213595","puri":"/rest/merchants/100856","path":"/resellers/1/customers/5/merchants/100856/transactions/11213595","transactionID":11213595,"currencies_id":1,"transactionUri":"/rest/transactions/11213595","ptype":"merchants","type":"transactions","currencyCode":"GBP"}

resulting api response from postman

{
    "id": 11213615,
    "pid": 103845,
    "uri": "/rest/transactions/11213615",
    "puri": "/rest/merchants/103845",
    "path": "/resellers/1/customers/2368/merchants/103845/transactions/11213615",
    "transactionID": 11213615,
    "currencies_id": 1,
    "transactionUri": "/rest/transactions/11213615",
    "ptype": "merchants",
    "type": "transactions",
    "currencyCode": null
  }

As you can see, currencyCode is still null - Why?! :(


Solution

  • As you can see, currencyCode is still null - Why?! :(

    I didn't work with "async.forEach" before but I think, that currencyCode is Null - because you call promise "getCurrencies", whith work asynchronously

    If you need to respond to client with result of work few promises - you have to collect promises to array, and then call Promise.all (which will return array of results of all promises)

    And you should send response in next block of chain. I think it should work

    connection.query(query)
    .then(function(items) {
        var arPromises = [];
        async.forEach(items, function(element) {
            element.ptype = 'merchants';
            element.type = 'transactions';
            element.currencyCode = null;
            arPromises.push(
                new Promise(function (resolve, reject) {
                    getCurrencies(element.currencies_id)
                        .then(function(result) {
                            element.currencyCode = result.currencyCode;
                            resolve(element);
                        }).catch(function (error) {
                            //do something with error
                            if(error)
                               reject(error);
                        });
                })
            );
        });
        return Promise.all(arPromises);
    }).then(function(items){
        res.send(items);
    }).catch(function (error) {
        if(error)
         console.log(error);
    });
    

    And my advice, finish chain with "catch" block - for avoid lost errors