FFI 中带有 Aff 回调的函数

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 端执行它。

Fyo*_*kin 5

从 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_