Search code examples
node.jsasync-awaitbabeljsecmascript-2016

How to retrieve async results from a Node.js VM script using ES7 syntax


I'm trying to implement a way to run "sequentially written" async JS code in a Node.js VM and gain access to the respective context objects. I try to use the coming ES7 await functionality, transpiled by babel.js.

As it seems to me, script.runInContext() is run in the background, while the main loop continues, thus I can't reach the results from the VM's context.

My sample code is the following:

var vm = require('vm');
var request = require('request-promise');
var babel = require("babel-core");

// VM context object
var contextCache = { 
    context: { 
        request: request 
    } 
};

// ES 7 code
var code = "var res = await request('http://www.google.de')";

// Wrap the code
code = "(async function() { " + code + " })()";

// Transpile code ES7 -> ES5
var regeneratedCode = babel.transform(code, { "ast": false, "presets": ["stage-0"] }).code

// Create VM context
var vmContext = new vm.createContext(contextCache.context);

// Create virtual script
var script = new vm.Script(regeneratedCode);

// Run script
script.runInContext(vmContext, {displayErrors: true, timeout: 30000});

// Check if variable was set -> Is undefined
console.log(contextCache.context.res);

Is there a way to retrieve asynchronous results from context evaluations in a synchronous way?

References:


Solution

  • I found a way to get this working... Basically it's using the this variable for the context object inside the executed code, and calling a callback function from inside as the last operation:

    var vm = require('vm');
    var babel = require("babel-core");
    
    // VM context object
    var context = {
        require: require,
        callback: function(error) {
            if (error) {
                console.log(error.stack);
            } else {
                console.log(this.response);
            }
        }
    };
    
    // ES 7 code
    var code = "var request = require('request-promise'); var response = await request({ url: 'https://graph.facebook.com/?id=http://news.ycombinator.com', json: true })";
    
    // Wrap the code
    code = "'use strict'; async function run() { try { " + code.replace(/var /g, "this.") + "; this.callback(null); } catch(error) { this.callback(error); } }; run.apply(this)";
    
    // Transpile code ES7 -> ES5
    var regeneratedCode = babel.transform(code, { "ast": false, "presets": ["stage-0"] }).code;
    
    // Create VM context
    var vmContext = new vm.createContext(context);
    
    // Create virtual script
    var script = new vm.Script(regeneratedCode);
    
    // Run script
    script.runInContext(vmContext, {displayErrors: true, timeout: 30000});