mar*_*osh 2 javascript asynchronous ffi purescript
我正在尝试包装一个 Javascript 库,其中我有一个A.bar(f)以 function 作为参数的方法f : B -> void。
由于我想用来bar执行一些异步计算,因此在 Purescript 方面,我有一个函数声明
foreign import foo :: Fn2 A (B -> Aff Unit) -> EffectFnAff Unit
Run Code Online (Sandbox Code Playgroud)
在其相应的 Javascript 文件中,我有类似的内容
exports.foo = function (a, f) {
return function (onError, onSuccess) {
a.bar(function (b) {
f(b)
})
return function (cancelError, cancelerError, cancelerSuccess) {
cancelerSuccess()
}
}
}
Run Code Online (Sandbox Code Playgroud)
我遇到的问题是这f(b)是一个Aff对象,我不知道如何在 Javascript 端执行它。
从 FFI-ied JavaScript 访问 PureScript 数据结构始终是一个坏主意。您不仅依赖于库的特定编写方式(没有编译器支持来捕获错误!),而且还依赖于编译器本身,因为运行时表示可能会从一个编译器版本更改为另一个编译器版本(请注意,这并不适用于EffectFnAff,因为它明确适用于 FFI 并根据)EffectFn2进行了仔细定义。
在 FFI 中表示有效计算的方法是Effect:
foreign import foo :: Fn2 A (B -> Effect Unit) -> EffectFnAff Unit
Run Code Online (Sandbox Code Playgroud)
现在可以按照您的方式从 JavaScript 调用此类函数 - as f(b)。
如果您希望库的使用者提供Aff,您要做的就是制作一个包装器:
foreign import foo_ :: Fn2 A (B -> Effect Unit) (EffectFnAff Unit)
foo :: A -> (B -> Aff Unit) -> Aff Unit
foo a f = fromEffectFnAff $ runFn2 foo_ a (launchAff_ <<< f)
Run Code Online (Sandbox Code Playgroud)
然后您只需导出包装器foo,而不是 FFI 导入foo_。
在某种程度上相关的说明中,我还建议取消EffectFnAff,因为您实际上并没有启动任何异步内容,而是始终调用cancelerSuccess().
因此,我会推荐这个:
// JavaScript
exports.foo = (a, f) => a.bar(f)
-- PureScript
foreign import foo_ :: EffectFn2 A (B -> Effect Unit) Unit
foo :: A -> (B -> Aff Unit) -> Aff Unit
foo a f = liftEffect $ runEffectFn2 foo_ a (launchAff_ <<< f)
Run Code Online (Sandbox Code Playgroud)
包装器仍然存在于两个地方 - 这是假设您出于自己的原因Aff需要将整个东西放入其中。Aff否则它可能只是foo = runEffectFn2 foo_