3

通常、同期動作が非同期動作よりも適切なノード 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 の実装を使用して、シェル スクリプトを呼び出すための次の非同期関数を作成しました。

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 ループの「待機」機能と、実装されたポーリング頻度を組み合わせようとしました。

4

1 に答える 1

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()(); ですべてラップする必要があります。これをファイバーで実行するには

于 2014-09-15T20:33:38.267 に答える