Js Deferred/Promise/Future по сравнению с функциональными языками вроде Scala
В основном я использую языки программирования, такие как Scala и JavaScript. Я пытаюсь понять сходство и различия в том, как асинхронное реактивное программирование используется на обоих языках. Можете ли вы мне помочь?
Я не беру какую-либо конкретную структуру Js Promise
, потому что, похоже, многие реализуют аналогичные спецификации (например, Promise/A). Пока что я использовал Q.
Кажется, что в Javascript мы вызываем Deferred
объект, который мы разрешаем для завершения Promise
.
В Scala кажется, что Promise
- это объект, который вы разрешаете получить монаду Future
.
Может ли кто-нибудь сказать мне, правильно ли это? Есть ли веская причина для другого использования термина Promise
между Js и Scala?
Кроме того, в Scala мы обычно связываем монуры Future
с дальнейшими вычислениями, используя такие операторы, как map
и flatMap
(также называемые bind
в Haskell). Что эквивалентно этим в Js?
Возможно, я ошибаюсь, но мне кажется, что в Js then
на a Promise
вид дескриптора как операторов map
, так и flatMap
? Если да, можно ли получить a promise of promise of result
в Js? Например, мы можем получить Future[Future[Result]]
в Scala (который может быть сплющен до Future[Result]
).
Является ли Js Promise
монадой? Кажется, что это так, даже если имена методов не соответствуют тем, которые мы находим в монадской литературе.
Ответы
Ответ 1
Да, и нет.
Пока очень похоже. С JavaScript Promises, которые соответствуют спецификации Promises/A + .then
, на самом деле не является монадическим связыванием и .map
и .flatMap
оба. Внутри обработчика .then
, когда вы возвращаете обещание, оно рекурсивно разворачивает его.
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");
});
(скрипка)
Вы правы, что стандартные библиотеки JS-обещаний и спецификация A + не имеют монадических promises. Они обсуждались, и существуют такие реализации, как fantasy-promises. Они следуют спецификации differnet и мало принимают. Также см. this. В дискуссионном форуме по языковому дизайну продолжается дискуссия - esdiscuss и монадический метод .chain
, который не имеет плоской карты и допускает монадический Promises, но вряд ли это сделает.
Это по прагматическим причинам. Реализуется текущий способ Promises. Редкие случаи, в которых вы действительно хотите Future[Future
, и обычно хотите продолжения, чтобы просто работать на этом языке. Promises "заимствовать" у монадов и "монадически" в некотором смысле. .then
очень близок к привязке, и в моей голове я использую их взаимозаменяемо:)
Невозможно иметь Promise[Promise[Value]]
как Future[Future[Value]]
в Scala с большинством библиотек обещаний. Вам придется обернуть его в объект и иметь 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
});
(скрипка)
Есть также ряд других меньших различий между ними, но обычно вы верны в своих утверждениях.
Ответ 2
Кажется, что в Javascript мы называем Отложенным объектом, который мы разрешаем, чтобы > выполнить обещание. В Scala кажется, что обещание - это объект, который вы разрешаете получить будущую монаду.
Может ли кто-нибудь сказать мне, правильно ли это? Есть ли веская причина для > различного использования термина Promise между Js и Scala?
В Scala Promise and 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, или вы связываете его с помощью карты, и вы пишете в Будущее, используя его. Обещание
В спецификациях JS Promise A + они объединяются вместе, Promise.then
используется как для цепочки, так и для получения значения для побочного эффекта (например, console.log), для записи вы будете использовать resolve
как фрагмент кода ниже
var promise = new Promise(function(resolve, reject){
Thread.sleep(10000);
resolve("promise completed");
}
Ответ 3
Я пытаюсь понять сходства и различия в том, как асинхронное реактивное программирование используется на обоих языках.
Этот документ здесь не сравнивает Javascript promises с Scala, а вместо этого Javascript promises с С++ С# и Python: https://github.com/KjellSchubert/promise-future-task. Я знаю, что это не то, о чем вы просили, но тем не менее это может дать вам несколько интересных указателей.
Ответ 4
В отличие от Scala, JS Promise не является монадой, из-за неявного "возможного" разворачивания нарушающего монадический закон. Однако вы можете реализовать монадическую семантику и функциональность на основе обратного вызова, служащие той же цели.
Смотрите, например, библиотеку cpsfy
.
Кроме того, существует структурное различие обусловлено .then
приема 2 функции, в то время как .chain
принимает только один. Тем не менее, chain
принимающая 2 или даже любое количество функций аргументов, может быть реализована, например, с помощью CPS
оболочки из 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
)