abb*_*bbr 114 javascript asynchronous synchronous node.js node-fibers
假设您维护一个公开函数的库getData
.您的用户将其调用以获取实际数据:
var output = getData();
在引擎盖下,数据保存在文件中,因此您getData
使用内置的Node.js 实现fs.readFileSync
.很明显这两个getData
和fs.readFileSync
是同步的功能.有一天,您被告知要将基础数据源切换到MongoDB等只能异步访问的仓库.您还被告知要避免惹恼您的用户,getData
不能更改API以仅返回承诺或要求回调参数.你如何满足这两个要求?
使用回调/保证的异步函数是JavasSript和Node.js的DNA.任何非平凡的JS应用程序都可能充满了这种编码风格.但这种做法很容易导致所谓的厄运金字塔回调.更糟糕的是,如果调用链中任何调用者中的任何代码都依赖于异步函数的结果,那么这些代码也必须包含在回调函数中,对调用者施加编码样式约束.我不时发现需要将异步功能(通常在第三方库中提供)封装到同步功能中,以避免大规模的全局重新分解.搜索关于此主题的解决方案通常最终得到Node Fibers或从中派生的npm包.但纤维无法解决我所面临的问题.甚至纤维作者提供的例子说明了这一缺陷:
...
Fiber(function() {
console.log('wait... ' + new Date);
sleep(1000);
console.log('ok... ' + new Date);
}).run();
console.log('back in main');
Run Code Online (Sandbox Code Playgroud)
实际产量:
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)
Run Code Online (Sandbox Code Playgroud)
如果函数Fiber确实将异步函数sleep转为同步,则输出应为:
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
Run Code Online (Sandbox Code Playgroud)
我在JSFiddle中创建了另一个简单的例子,并寻找产生预期输出的代码.我会接受一个只适用于Node.js的解决方案,所以你可以自由地要求任何npm包,尽管不能在JSFiddle中工作.
abb*_*bbr 101
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)
Run Code Online (Sandbox Code Playgroud)
(免责声明:我是该作者的共同作者deasync
.该模块是在发布此问题后创建的,但未找到可行的提案.)
小智 5
还有一个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);
}
});
});
}
Run Code Online (Sandbox Code Playgroud)
参考链接:https://www.npmjs.com/package/sync
你必须使用承诺:
const asyncOperation = () => {
return new Promise((resolve, reject) => {
setTimeout(()=>{resolve("hi")}, 3000)
})
}
const asyncFunction = async () => {
return await asyncOperation();
}
const topDog = () => {
asyncFunction().then((res) => {
console.log(res);
});
}
Run Code Online (Sandbox Code Playgroud)
我更喜欢箭头函数定义。但是任何形式为“() => {...}”的字符串也可以写成“function () {...}”
所以尽管调用了异步函数,topDog 并不是异步的。
编辑:我意识到很多时候你需要将异步函数包装在一个同步函数中是在一个控制器中。对于这些情况,这里有一个派对技巧:
const getDemSweetDataz = (req, res) => {
(async () => {
try{
res.status(200).json(
await asyncOperation()
);
}
catch(e){
res.status(500).json(serviceResponse); //or whatever
}
})() //So we defined and immediately called this async function.
}
Run Code Online (Sandbox Code Playgroud)
将此与回调一起使用,您可以进行不使用承诺的包装:
const asyncOperation = () => {
return new Promise((resolve, reject) => {
setTimeout(()=>{resolve("hi")}, 3000)
})
}
const asyncFunction = async (callback) => {
let res = await asyncOperation();
callback(res);
}
const topDog = () => {
let callback = (res) => {
console.log(res);
};
(async () => {
await asyncFunction(callback)
})()
}
Run Code Online (Sandbox Code Playgroud)
通过将此技巧应用于 EventEmitter,您可以获得相同的结果。在我定义回调的地方定义 EventEmitter 的侦听器,并在我调用回调的地方发出事件。
归档时间: |
|
查看次数: |
113782 次 |
最近记录: |