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

假设您维护一个公开函数的库。您的用户调用它以获取实际数据:

引擎盖下的数据保存在一个文件中,因此您使用Node.js内置.很明显,两者都是同步功能。有一天,你被告知要将基础数据源切换到一个存储库,比如MongoDB,它只能异步访问。您还被告知要避免惹恼您的用户,API不能更改为仅返回承诺或要求回调参数。您如何满足这两个要求?getDatavar output = getData();getDatafs.readFileSyncgetDatafs.readFileSyncgetData

使用回调/承诺的异步函数是JavasSript和Node.js的DNA。任何重要的JS应用程序都可能渗透到这种编码风格中。但这种做法很容易导致所谓的厄运回调金字塔。更糟糕的是,如果调用链中任何调用方中的任何代码依赖于异步函数的结果,则这些代码也必须包装在回调函数中,从而对调用方施加编码样式约束。我不时发现需要将异步函数(通常在第三方库中提供)封装到同步函数中,以避免大规模的全局重构。搜索有关此主题的解决方案通常最终会得到节点光纤或从中派生的 npm 包。但纤维无法解决我面临的问题。甚至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因此您可以自由地需要任何npm包,尽管在JSFiddle中不起作用。


答案 1

deasync 将异步函数转换为同步,通过在 JavaScript 层调用 Node.js事件循环,使用阻塞机制实现。因此,deasync 仅阻止后续代码运行,而不会阻塞整个线程,也不会导致忙等待。有了这个模块,这里是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


答案 2

还有一个 npm 同步模块。用于同步执行查询的过程。

当您想要以同步方式运行并行查询时,节点限制这样做,因为它从不等待响应。同步模块非常适合这种解决方案。

示例代码

/*require sync module*/
var Sync = require('sync');
    app.get('/',function(req,res,next){
      story.find().exec(function(err,data){
        var sync_function_data = find_user.sync(null, {name: "sanjeev"});
          res.send({story:data,user:sync_function_data});
        });
    });


    /*****sync function defined here *******/
    function find_user(req_json, callback) {
        process.nextTick(function () {

            users.find(req_json,function (err,data)
            {
                if (!err) {
                    callback(null, data);
                } else {
                    callback(null, err);
                }
            });
        });
    }

参考链接: https://www.npmjs.com/package/sync