将承诺包装到同步功能中

时间:2014-09-15 19:38:33

标签: javascript node.js asynchronous synchronous rsvp-promise

我正在编写节点CLI,其中同步行为通常比异步更合适,我希望能够利用以下约定:

 # Write functional code as an async function which returns a Promise
 function foobar() { ... }
 # Uses async function but blocks on promise fulfillments
 function foobarSync() { ... }

例如 - 使用RSVP promise实现 - 我编写了以下用于调用shell脚本的异步函数:

var shell = function (params,options) {
    options = extend({timeout: 4000},options);
    var commandResponse = '';
    var errorMessage ='';
    // resolve with a promise
    return new RSVP.Promise(function(resolve,reject) {
        var spawn = require('child_process').spawn;
        var timeout = setTimeout(function() {
            reject(new Error('Timed out')); // fulfil promise
        }, options.timeout);
        try {
            var shellCommand = spawn(params.shift(),params);
        } catch (err) {
            clearTimeout(timeout);
            reject(err); // fulfil promise
        }
        shellCommand.stdout.setEncoding('utf8');
        shellCommand.stderr.setEncoding('utf8');
        shellCommand.stdout.on('data', function (data) {
            commandResponse = commandResponse + data;
        });
        shellCommand.stderr.on('data', function (data) {
            errorMessage = errorMessage + data;
        });
        shellCommand.on('close', function (code) {
            if(code !== 0) {
                clearTimeout(timeout);
                reject({code:code, message:errorMessage}); // fulfil promise
            } else {
                clearTimeout(timeout);
                resolve(commandResponse); // fulfil promise
            }
        });
    }); 
};

这有效,现在我想同步:

 # Works
 shell(['ls','-l']).then( function (results) {
      console.log('Result was: %s', results);
 });
 # Would like to see work
 var results = shellSync(['ls','-l']);

我认为对shellSync有用的是:

var shellSync = function (params,options) {
    options = extend({pollingInterval: 100},options);
    var shellResults = null;
    shell(params,options).then(
        function(results) {
            console.log('Results: %s', results);
            shellResults = results;
            // return results;
        },
        function(err) {
            console.log('Error: %s', err);
            shellResults = err;
            // return err;
        }
    );

    while(!shellResults) {
        // wait until a Promise is returned or broken (and sets the shellResults variable)
    }
    return shellResults;
};

不幸的是,这只是运行,永远不会返回。我可能代替while循环,我实现了一个轮询间隔来执行条件语句:

    var polling = setInterval(function() {
        // return once shellResults is set; 
        // this setting takes place when either a resolve() or reject() 
        // is called in Promise
        if(shellResults) {
            console.log('results are available');
            clearInterval(polling);
            return shellResults; 
        }
    },options.pollingInterval);

    while(1) {
        // wait 
    }

当然,删除while循环会导致函数立即返回(具有尚未实现的承诺)。所以我试着结合"等待"实现轮询频率的while循环功能

1 个答案:

答案 0 :(得分:0)

最简单的方法是在内部代码上使用同步API,如果您希望它同步,但是您想将异步代码包装为同步,对吗?

我认为这可以通过纤维https://www.npmjs.org/package/fibers),或者更具体地说,期货来实现,这是一个小例子

var Future = require("fibers/future");

// async API, will be wrapped by syncFoo
var asyncFoo = function(cb) {
  setTimeout(function() {
    cb("foo");
  }, 1500);
};

var syncFoo = function() {
  var future = new Future();
  console.log("asyncFoo will be called");
  asyncFoo(function(x) {
    future.return(x);
  });
  console.log("asyncFoo ended");
  return future.wait();
};

(function() {
console.log("* Syncfoo will be called");
syncFoo();
console.log("* Syncfoo ended");

console.log("* Syncfoo will be called again");
syncFoo();
console.log("* Syncfoo ended again");

}).future()();

更具体的代码:


var shellSync = function(params, options) {
    var future = new Future();

    options = extend({pollingInterval: 100},options);
    var shellResults = null;
    shell(params,options).then(
        function(results) {
            console.log('Results: %s', results);
            future.return({results: results});
        },
        function(err) {
            console.log('Error: %s', err);
            future.return({err: err});
        }
    );

    var ret = future.wait();
    if (ret.err) {
      throw ret.err;
    } else {
      return ret.results;
    }
};

编辑注释:你应该全部包装(function(){...})。future()();在光纤上运行它