Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/446.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 函数式编程和异步/异步编程_Javascript_Node.js_Asynchronous_Functional Programming_Ramda.js - Fatal编程技术网

Javascript 函数式编程和异步/异步编程

Javascript 函数式编程和异步/异步编程,javascript,node.js,asynchronous,functional-programming,ramda.js,Javascript,Node.js,Asynchronous,Functional Programming,Ramda.js,我正在将一些旧的节点模块重构成一种更具功能性的风格。说到FP,我就像一个大二新生:)我一直被挂断的地方是处理大型异步流。下面是一个示例,我向数据库发出请求,然后缓存响应: //一些外部xhr/promise库 const fetchFromDb=make=>{ 返回新承诺(解决=>{ console.log('Simulate async db request…');//此处仅模拟异步请求/响应。 设置超时(()=>{ log('Simulate db response…'); 解析({mak

我正在将一些旧的节点模块重构成一种更具功能性的风格。说到FP,我就像一个大二新生:)我一直被挂断的地方是处理大型异步流。下面是一个示例,我向数据库发出请求,然后缓存响应:

//一些外部xhr/promise库
const fetchFromDb=make=>{
返回新承诺(解决=>{
console.log('Simulate async db request…');//此处仅模拟异步请求/响应。
设置超时(()=>{
log('Simulate db response…');
解析({make:'toyota',data:'stuff'});
}, 100);
});
};
//记忆fn
//这将缓存对getCarData(x)的响应,以便无论何时再次使用“x”调用它,都会返回相同的响应。
const getCarData=R.memoizeWith(R.identity,(carMake,response)=>response.data);
//这个函数是纯函数吗?还是设置了范围之外的内容(即getCarData)?
const getCarDataFromDb=(carMake)=>{
返回fetchFromDb(carMake).then(getCarData.bind(null,carMake));
//注意:此返回语句基本上与以下语句相同:
//返回fetchFromDb(carMake).then(result=>getCarData(carMake,result));
};
//初始化“toyota”数据请求
const toyota=getCarDataFromDb('toyota');//无论发生什么事都必须打电话
//方法#1-仅依靠表格
log(`toyota的值为:${toyota.toString()}`);
toyota.then(d=>console.log(`Value-in-thenable:${d}`));//->表中的值:stuff
//方法#2-确保在db响应之前不要调用此fn。
setTimeout(()=>{
const car=getCarData(‘丰田’);//太好了!
log(`later,car is:${car}`);//->'later,car is:stuff'
}, 200);
问题1 关于是否有副作用,这几乎是一个哲学问题。调用它会更新备忘录缓存。但这本身没有明显的副作用。所以我想说这实际上是纯的

更新:一条评论指出,当它调用IO时,它永远不可能是纯粹的。这是正确的。但这就是这种行为的本质。它作为一个纯函数没有意义。我上面的回答只是关于副作用,而不是关于纯度

问题2 我不能代表整个FP社区发言,但我可以告诉你,Ramda团队(免责声明:我是Ramda的作者)更喜欢避免
Promise
s,更喜欢
Future
s或
Task
s等更合法的类型。但这里的问题与那些取代了
Promise
s的类型相同。(有关这些问题的更多信息,请参见下文。)

一般来说 这里有一个中心点:如果您正在进行异步编程,它将传播到涉及它的应用程序的每一个部分。你不会做任何改变这个基本事实的事情。使用
Promise
s/
Task
s/
Future
s有助于避免一些基于回调的代码样板,但它要求您将响应/拒绝后代码放入
然后
/
映射
函数中。使用
async/await
可以帮助您避免一些基于承诺的代码样板,但它要求您将响应后/拒绝代码放入
async
函数中。如果有一天我们在
async/await
的基础上再加上一层其他的东西,那么它很可能具有相同的特性

(虽然我建议您关注
未来
s或
任务
s,而不是
承诺
s,但下面我只讨论承诺。不管怎样,同样的想法都应该适用。)

我的建议 如果要记忆任何内容,请记忆生成的
承诺

无论如何处理异步,都必须将依赖于异步调用结果的代码放入函数中。我假设第二种方法的
setTimeout
只是为了演示:使用timeout在网络上等待DB结果非常容易出错。但是,即使使用
setTimeout
,其余的代码也会在
setTimeout
回调中运行

因此,与其试图区分数据已缓存和未缓存的情况,不如在任何地方都使用相同的技术:
myPromise.then(…mycode…
)。可能看起来像这样:

//getCarData::String->Promise AutoInfo
const getCarData=R.memoizeWith(R.identity,make=>newpromise(resolve=>{
log('模拟异步数据库请求…')
设置超时(()=>{
log('模拟数据库响应…')
解析({make:'toyota',data:'stuff'});
}, 100)
})
)
getCarData('toyota')。然后(carData=>{
log('现在我们可以走了',carData)
//任何依赖于carData的代码
})
//后来
getCarData('toyota')。然后(carData=>{
console.log('现在已缓存',carData)
})
getCarFromDb是上面写的纯函数吗

不。几乎所有使用I/O的东西都是不纯净的。数据库中的数据可能会更改,请求可能会失败,因此它不能可靠地保证返回一致的值

以这种方式使用备忘录是FP反模式吗?也就是说,从一个带有响应的thenable调用它,以便将来对同一个方法的调用返回缓存的值

这绝对是一个异步反模式。在您的方法#2中,您正在创建一个竞争条件,如果DB查询在不到200毫秒的时间内完成,操作将成功,如果超过200毫秒,操作将失败。您在代码中标记了一行“太好了!”因为您能够同步检索数据。这向我表明,您正在寻找一种绕过异步问题的方法,而不是正面面对它

您使用
bind
和“欺骗”
memoizeWith
来存储您要存储的值的方式