4

我正在尝试实现一种在 Node.js VM 中运行“顺序编写”异步 JS 代码并获得对相应上下文对象的访问权限的方法。我尝试使用即将到来的 ES7await功能,由babel.js.

在我看来,script.runInContext()在后台运行,而主循环继续,因此我无法从 VM 的上下文中获得结果。

我的示例代码如下:

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

有没有办法以同步的方式从上下文评估中检索异步结果?

参考:

4

4 回答 4

4

我找到了一种让它工作的方法......基本上它this在执行的代码中使用上下文对象的变量,并从内部调用回调函数作为最后一个操作:

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});
于 2015-12-08T19:27:39.857 回答
1

这个想法是如果脚本上有一些异步操作,则知道脚本何时结束。我们可以通过将 Promise 的 resolve 函数附加到上下文中来在 Promise 中的 vm 上执行脚本。然后在脚本上执行附加在上下文中的解析函数。这保证了你在这个承诺被解决后,你现在可以获取异步函数的结果。

const vm = require('vm');

(async () => {
  const sandbox = {
    a: 1
  };
  await new Promise(resolve => {
    sandbox.resolve = resolve;
    const code = 'Promise.resolve(2).then(result => {a = result; resolve();})';
    const script = new vm.Script(code);
    const context = new vm.createContext(sandbox);
    script.runInContext(context);
  });
  console.log(sandbox.a); // 2
})();

要点在这里

于 2020-02-13T21:30:26.040 回答
0

而不是替换var this.您可以删除var.

这将使变量对上下文是全局的,然后可以在 vm 外部访问。

于 2020-07-23T07:46:38.777 回答
0
async function runScript(code, context = {}, options = {}) {
  return new Promise((resolve, reject) => {
    const { timeout = 120 * 1000, breakOnSigint = true } = options;
    const script = new Script(`(async()=>{${code}})()`);
    script.runInContext(createContext({
      ...context,
      resolve,
      reject,
    }), {
      timeout,
      breakOnSigint,
    });
  });
}

演示

await runScript('some code; resolve();')
于 2021-11-05T06:47:53.030 回答