Js Deferred/Promise/Future 与 Scala 等函数式语言相比

Posted

技术标签:

【中文标题】Js Deferred/Promise/Future 与 Scala 等函数式语言相比【英文标题】:Js Deferred/Promise/Future compared to functional languages like Scala 【发布时间】:2014-05-08 15:01:51 【问题描述】:

我主要使用 Scala 和 javascript 等编程语言。我试图了解异步反应式编程在两种语言中的使用方式的异同。你能帮帮我吗?

我没有采用任何特定的 Js Promise 框架,因为它似乎很多实现了类似的规范(如 Promise/A)。到目前为止我只用过Q。


似乎在 Javascript 中,我们调用了 Deferred,我们解析为完成 Promise 的对象。 在 Scala 中,Promise 似乎是您解析以获得 Future monad 的对象。

谁能告诉我这是否正确?在 Js 和 Scala 之间,Promise 一词的不同用法有什么好的理由吗?


此外,在 Scala 中,我们通常使用 mapflatMap 等运算符(在 Haskell 中也称为 bind)将 Future monad 与进一步的计算联系起来。 Js中的这些等价物是什么?

我可能错了,但在我看来,在 Js 中 Promise 上的 then 处理 mapflatMap 运算符对吗?如果是这样,是否有可能在Js中获得promise of promise of result?就像我们可以在 Scala 中获得 Future[Future[Result]] 一样(无论如何都可以将其展平为 Future[Result])。


Js Promise 是单子吗?即使方法名称与我们在 monad 文献中找到的名称不匹配,它似乎也是如此。

【问题讨论】:

【参考方案1】:

是的,也不是。

虽然极其相似。对于符合 Promises/A+ 规范的 JavaScript Promises,.then 并不是真正的单子绑定,.map.flatMap 两者都执行。在 .then 处理程序中,当您返回一个 Promise 时,它​​将递归地解包它。

Promise.delay(1000).then(function() 
    return Promise.delay(1000).then(function () 
        return Promise.delay(2000);
    ).then(function () 
        return Promise.delay(5000)
    );
).then(function () 
    alert("This is only shown after 8 seconds and not one");
);

(fiddle)

您是正确的,标准 JS 承诺库和 A+ 规范不具有单子承诺。它们已经被讨论过,并且存在像 fantasy-promises 这样的实现。他们遵循不同的规范并且很少采用。另见this。在语言设计讨论论坛中一直在讨论它 - esdiscuss 和一个不平面映射并允许 monadic promises 的 monadic .chain 方法被考虑但不太可能实现。

这是出于务实的原因。当前实现承诺的方式非常有用。很少有你真正想要Future[Future 的情况,通常你希望延续只在该语言中工作。 Promise 从 monad 中“借用”,并且在某种意义上本身就是“monadic”。 .then 非常接近绑定,在我的脑海中我可以互换使用它们:)

对于大多数 Promise 库,在 Scala 中不可能有像 Future[Future[Value]] 这样的 Promise[Promise[Value]]。您必须将其包装在一个对象中并拥有Promise[Container[Promise[Value]]]

Promise.delay(1000).then(function () 
    return Promise.delay(1000).then(function () 
        return 
            wrap: Promise.delay(2000).then(function () 
                return Promise.delay(5000);
            )
        ;
    );
).then(function () 
    alert("This logs after 1 second");
    // I've also not seen a really solid use case 
    // except TypeScript type inference which is meh
);

(fiddle)

两者之间还有许多其他较小的差异,但通常您的断言是正确的。

【讨论】:

谢谢。对不起,我忘了接受你的好回答。我猜 JS 选择了务实的道路,主要是因为它是一种动态类型的语言,不像 Scala Benjamin,好的,但为什么是 8 秒和 1 秒?当然是 9 秒和 2 秒。更新了小提琴 - 1,2。【参考方案2】:

似乎在 Javascript 中,我们将解析的对象称为 Deferred >完成 Promise。在 Scala 中,Promise 似乎是您>解决以获得 Future monad 的对象。

谁能告诉我这是否正确? >Js 和 Scala 对 Promise 一词的不同用法有什么好的理由吗?

在 Scala 中,Promise 和 Future 具有分离的功能,Future 是一个异步计算容器,它会在将来返回一些值,Promise 是异步计算的编写部分,您可以执行以下操作

val promise = Promise[String]
val future1 = promise.future
val future2 = future1.map  case s => println(s); s 

future2.onSuccess  case s => println(s + " 2nd time") 

promise.success("promise completed")

执行最后一条语句后,输出将是

promise completed
promise completed 2nd time

在 Scala 中,您可以使用 onComplete 从 Future 读取值,或者使用 map 将其链接起来,然后使用 Promise 对应物写入 Future

在 JS Promise A+ 规范中,它们被捆绑在一起,Promise.then 用于链接和检索副作用的值(例如 console.log),写你将使用 resolve 就像代码 sn-p下面

var promise = new Promise(function(resolve, reject)
    Thread.sleep(10000);
    resolve("promise completed");

【讨论】:

【参考方案3】:

我试图了解异步响应式编程在两种语言中的使用方式的异同。

这里的文档没有将 Javascript Promise 与 Scala 进行比较,而是将 Javascript Promise 与 C++ C# 和 Python 进行比较:https://github.com/KjellSchubert/promise-future-task。我知道这并不完全是您所要求的,但这可能会给您一些有趣的建议。

【讨论】:

【参考方案4】:

与 Scala 相比, the JS Promise is not a monad, due to the implicit "thenable" unwrapping breaking monadic law。 但是,您可以实现基于回调的一元语义和功能,以达到相同的目的。

参见例如the cpsfy library.

另外,由于.then 接受2 个函数,而.chain 只接受1 个函数,存在结构上的差异。但是,可以实现一个chain 接受 2 甚至任意数量的参数函数,例如和 CPS wrapper from cpsfy:

//function returning CPS function with 2 callbacks
const readFileCps = file => (onRes, onErr) =>  
  require('fs').readFile(file, (err, content) => 
    err ? onErr(err) : onRes(content)
  )

// CPS wraps a CPS function to provide the API methods
const getLines = CPS(readFileCps('name.txt'))
  // map applies function to the file content
  .map(file => file.trim()) 
  .filter(file => file.length > 0)
  // chain applies function that returns CPS function
  .chain(file => readFileCps(file))
  .map(text => text.split('\n'))
// => CPS function with 2 callbacks

// To use, simply pass callbacks in the same order
getLines(
  lines => console.log(lines),  // onRes callback
  err => console.error(err)  // onErr callback
)

【讨论】:

以上是关于Js Deferred/Promise/Future 与 Scala 等函数式语言相比的主要内容,如果未能解决你的问题,请参考以下文章

如何在一个js中调用另一个js,以及载入多个有依赖关系的js

js文件如何引用外部js

在js中获取作成者

怎么调用外部js文件?

web--JS 基础实例汇总

怎么合并js