1

I am new to node and writing a small application. I haven't used a language as asynchronous as this on the server before and have myself in a bit of a pickle. I need to take a string, query a table for an id, then insert in a second table using the result, then return a string from the funtion two levels up. I have a custom dao I use for the db stuff. Here is the function where it all happens:

function generateToken(data, userId, client) {
    var random = Math.floor(Math.random() * 100001);
    var sha256 = crypto.createHmac("sha256", random );
    var token = sha256.update(data).digest("base64");
    var query = dao.select(
        'auth.apps', 
        {  
            name: client.name, 
            version: client.version, 
            subversion: client.subversion, 
            patch: client.patch    
        }
    ).done(
        function(result) {
            dao.insert(
                'auth.tokens',  
                { 
                    user_id:userId, 
                    app_id: result.rows[0].id, 
                    token:token         
                } 
             ); 
             return "mmmm yellllo";
         }
    ); 

    var ret_val = await(query);
    console.log("Token return: " + ret_val);

    return ret_val;
}

and here is the relevant part of my dao for select:

dbo.prototype.select = function(table, where, order_by) {

    var where_clause = this.construct_where(where);
    var sql = 'SELECT * FROM ' + table + ' WHERE ' + where_clause;

    if(order_by  !== undefined) {
        sql = sql + ' ORDER BY ' + order_by;
    };  
    var result = this.pool.query(sql);

    return result;
};

and insert:

dbo.prototype.insert= function(table, values) {
    var key_list='', value_list = ''; 

    for( var k in values) 
    {   
        key_list = key_list + ', ' + k;
        value_list = value_list + ", '" + values[k] + "'";
    }   

    // chop off comma space
    key_list = key_list.substring(2);
    value_list = value_list.substring(2);

    var sql = 'INSERT INTO ' + table + '(' + key_list + ') VALUES(' + value_list + ') RETURNING id';

    var result = this.pool.query(sql).catch(function(error) {
        console.log("SQL:" + sql + " error:" + error);
    });

    return result;
};

How do unwind the double promise. I want the generateToken function to return the token variable but only after the insert query has finished.

Frank Conry
  • 2,694
  • 3
  • 29
  • 35
  • Easiest way is to use Promises. See http://stackoverflow.com/a/41567664/3830485 – SaiNageswar S Feb 02 '17 at 04:36
  • The thing is `generateToken` integrates with an external library, so I don't have the flexibility to return a promise, I *have to* return the token string and I don't want to do that unless it has been saved in the db. – Frank Conry Feb 02 '17 at 04:41
  • @FrankConry So as I understand your generateToken function is already used by someone so you are not able to change the structure, right? – Antonio Narkevich Feb 02 '17 at 05:09
  • yes, it's part of the restify oauth framework – Frank Conry Feb 02 '17 at 05:10

1 Answers1

1

There is a library named deasync. And the motivation to create it was to solve the situations when

API cannot be changed to return merely a promise or demand a callback parameter

So this is the primary and probably the only use case. Because in general Node.js should stay async.

To do the trick you basically should write a function that accepts a callback and then wrap it with deasync as follows:

var deasync = require('deasync');

//It can still take the params before the callback
var asyncGenerateToken = function (data, userId, client, callback) {
 var token = 'abc';

 //Async operation starts here
 setTimeout(function () {
  //Async operation is finished, now we can return the token
  //Don't forget that the error is 1st arg, data is the 2nd
  callback(null, token);
 }, 1000);
};

var generateToken = deasync(asyncGenerateToken);

//We'll retrieve a token only after a second of waiting
var token = generateToken('my data', 'my user id', 'my client');
console.log(token);

Hope this helps.

Antonio Narkevich
  • 4,206
  • 18
  • 28