将 promise 包装到 Sync 函数中
Posted
技术标签:
【中文标题】将 promise 包装到 Sync 函数中【英文标题】:Wrapping promise into a Sync function 【发布时间】:2014-11-09 10:19:21 【问题描述】:我正在编写一个节点 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 循环的“等待”功能与实现的轮询频率结合起来
【问题讨论】:
好吧,也许是个疯狂的想法,但是使用 traceur 你可以使用 EC6 的 await 关键字。它会将你的代码重新编译成一个奇怪的状态机,但这可能是一个很容易解决你的情况的方法。 这在 JS 本身是不可能的。您需要一个用于在外部添加此功能的方法的插件。不知道这个模块好不好用,不建议用,你可以看看deasync @t.niese 我确实简单地看过它......我的第一印象是它可能无法真正满足我的需求。我还发现了一个名为 exec-sync 的 NPM 模块,它在 OSX 上运行良好(它可以编译到每个平台上),但在 Ubuntu 上似乎失败了。 :( @DavidMulder 我现在很想避免使用 EC6。它在我的清单上,要完全包裹起来,但如果可能的话,我现在想把自己限制在 Node 上(没有 Harmony)。我确实注意到 Task.js 看起来像是 ES6 对这个问题的回答。 因为js
本身不是多线程的。只要您在while(!shellResults)
块中,就不会执行其他js
代码。
【参考方案1】:
如果您希望同步代码,最简单的方法是在您的内部代码上使用同步 API,但您想将异步代码包装为同步,对吧?
我认为这可以使用 fibers (https://www.npmjs.org/package/fibers) 来实现,或者更具体地说,futures,一个小例子
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()();在光纤上运行它
【讨论】:
看起来很有希望。迫不及待想试试,但必须先睡觉。会在早上尝试第一件事。 :) 第一次尝试使用您的代码示例我在调用future.wait()
时收到“无法等待没有光纤”的错误。将进行调查。
错误。在分配的时间内无法理解它。这是我的要点:gist.github.com/ksnyde/d78cc01f62def105f1a0
你应该把所有的东西都包裹在 (function() ).future()();以上是关于将 promise 包装到 Sync 函数中的主要内容,如果未能解决你的问题,请参考以下文章
将对象绑定到 Promise.then() 参数的正确方法 [重复]
将 fetch 包装在 Promise 中有啥好处? [复制]