synchronous behaviour in node.js

So... I know this is NOT the right way... but I have a box that I've got myself into which appears easiest to navigate if I can find a way to have a "get" request behave synchronously in node.js

Yes, I've tried node-sync (which looked like it should work)

Scenario is this

  • reading a parameterized string from a file (done, works fine)
  • recursively tokenize the string, chunking it into keys & values (done, works fine)
  • making a call to Google Translate to translate each value (breaks!)
  • re-assembling the parameterized string and writing it back to an output file (done, works fine)

I know how to do the call to google translate (can easily do it for whole strings), but can't get my mind wrapped around how to get the ordering of an async callback lined up with the recursion on the parameterized string necessary for the data set I have.

This would be simple if I could make the call to the web service behave as if it were synchronous. e.g.

read string (while not EOF)
    recurse (get the next token)
        translate the fragment (which is done with request.get()
        add translated fragment to the current string
    assemble translated string
    write to file

The node-sync examples worked, but I couldn't get it to work with request.get().

Any suggestions?

EXCERPT

// this function gets passed in a string and recursively pulls out and
// attempts to translate tokens.  It needs to do this as the data source
// contains "key" = "fragment" + $(paramter1) + fragment + $(parameter2) etc

function getTokenSegment(sourceString){

s = sourceString;  //lazy

// if the string contains a parameter 
if (s.indexOf(leadingDelimiter) != -1) {    

    // extract the tokens...omitted the error checking (which all works)
    translatedToken = syncTranslate(token);   // <--- THIS IS WHAT I WANT...
    parameter = s.substring(token.length+1, s.indexOf(trailingDelimiter)+1);
    remainder = s.substring(token.length + parameter.length+1, s.length);

    translatedString = translatedString + translatedToken + parameter 

        // recursive call to get the rest of the string
        + getTokenSegment(remainder);
}
else {  

    // the remainder of the string can be translated intact
    translatedToken = syncTranslate(s);
    translatedString = translatedString + translatedToken;
}
return (translatedString);
}

function syncTranslate(stringToTranslate) {
    var sync = require('sync');
sync(function(){
    var result = translate.sync(null, {key: key, q: stringToTranslate, target: language});
})
    return result;  
}


// translate module is from Mike Holly -- https://github.com/mikejholly/node-google-translate
// and worked perfectly when I used it on non-parameterized strings.  only edit is the NULL as the
// first parameter in the callback, copied from the node-sync simple example]


var request = require('request')
  , _ = require('underscore')
  , querystring = require('querystring')
  , util = require('util')

module.exports = function(opts, callback) {

// parse & default the arguments
opts = _.defaults(opts, {
    source: 'en',
    target: 'fr',
    key: 'secret', 
    sourceText: 'text'
});

var url = 'https://www.googleapis.com/language/translate/v2?' + querystring.stringify(opts);

request.get(url, function(err, response, body){

   if (err) throw err;

   // parse the returned JSON
       var json = JSON.parse(body);
       if (json.error) {
          throw json.error.message;
    }
    var strings = util.isArray(opts.sourceText) ? opts.sourceText : [opts.sourceText];

    var result = {};
var translatedString = '';
    strings.forEach(function(original, i){
      result[original] = json.data.translations[i].translatedText;
  translatedString = result[original];

   });

   callback(null, translatedString);

   });

};

I am guessing here, because your question is not clear enough. I appreciate your difficulties because it's hard to talk about software. I think you forgot to wrap your code in a Sync block:

// Run in a fiber
Sync(function(){

    // your code

})

Source: https://github.com/0ctave/node-sync#examples

I give you a hint. Try the most simple task first. Try to translate only one word, then use the translated word. Do this thing with request.get() within Sync. When this works, you trust the software more and are more confident to continue with more complicated things.

A very popular module for writing sync code in node.js is async. For your problem I would try the waterfall function:

async.waterfall([
    // your first function
    function(callback){
        callback(null, 'one', 'two');
    },
    // your second function
    function(arg1, arg2, callback){
        callback(null, 'three');
    },
    // your third function
    function(arg1, callback){
        // arg1 now equals 'three'
        callback(null, 'done');
    }
], function (err, result) {
   // result now equals 'done'    
});