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
)