小编典典

如何在Node.js或Javascript中将异步函数调用包装到同步函数中?

javascript

假设您维护一个公开一个函数的库getData。您的用户调用它来获取实际数据:var output = getData(); 数据被保存在文件中,因此您可以getData使用内置的Node.js来实现fs.readFileSync。很明显这两个getDatafs.readFileSync是同步的功能。有一天,您被告知将基础数据源切换到只能异步访问的仓库(例如MongoDB)。还被告知要避免惹恼您的用户,getDataAPI不能更改为仅返回promise或要求回调参数。您如何满足这两个要求?

使用回调/承诺的异步功能是JavasSript和Node.js的DNA。任何不平凡的JS应用程序都可能会渗透这种编码样式。但是这种做法很容易导致所谓的厄运回响金字塔。更糟糕的是,如果调用链中任何调用方中的任何代码都取决于异步函数的结果,则这些代码也必须包装在回调函数中,从而在调用方上施加编码样式约束。我不时发现有必要将异步功能(通常在第三方库中提供)封装到同步功能中,以避免大规模的全局重构。在这个问题上寻找解决方案通常以节点光纤结束或从中派生的npm软件包。但是Fibers不能解决我面临的问题。甚至由Fibers作者提供的示例也说明了这一缺陷:

...
Fiber(function() {
    console.log('wait... ' + new Date);
    sleep(1000);
    console.log('ok... ' + new Date);
}).run();
console.log('back in main');

实际输出:

wait... Fri Jan 21 2011 22:42:04 GMT+0900 (JST)
back in main
ok... Fri Jan 21 2011 22:42:05 GMT+0900 (JST)

如果功能Fiber确实将异步功能睡眠转变为同步,则输出应为:

wait... Fri Jan 21 2011 22:42:04 GMT+0900 (JST)
ok... Fri Jan 21 2011 22:42:05 GMT+0900 (JST)
back in main

我在JSFiddle中创建了另一个简单的示例,并寻找产生预期输出的代码。我将接受仅在Node.js中可用的解决方案,因此尽管在JSFiddle中不起作用,您也可以自由地要求使用任何npm软件包。


阅读 368

收藏
2020-05-01

共1个答案

小编典典

deasync通过在JavaScript层调用Node.js事件循环,通过阻止机制将异步功能转换为同步功能。结果,不同步仅阻止后续代码运行,而不阻止整个线程,也不确保繁忙的等待。使用此模块,这是jsFiddle挑战的答案:

function AnticipatedSyncFunction(){
  var ret;
  setTimeout(function(){
      ret = "hello";
  },3000);
  while(ret === undefined) {
    require('deasync').runLoopOnce();
  }
  return ret;    
}


var output = AnticipatedSyncFunction();
//expected: output=hello (after waiting for 3 sec)
console.log("output="+output);
//actual: output=hello (after waiting for 3 sec)

(免责声明:我是的合著者deasync。该模块是在发布此问题后创建的,没有找到可行的建议。)

2020-05-01