Skip to content

Task vs Promise #3672

Closed
Closed

Description

@dbrattli

Description

It's currently hard to use Fable to write code that uses Task and compile to both Python and JS/TS. Python translates Task to native Task/Future awaitables (async / await) while for TS/JS you need to use JS.Promise.

This makes the code base filled with #if/#else even if you try to rename Promise to Task. Is it time to support Task in Fable for JS/TS that the task CE is native for F#? Ref: dbrattli/Fable.Giraffe#13 (comment)

Activity

  1. MangelMaxime commented on Dec 18, 2023

    @MangelMaxime
    Member

    Are Task in .NET hot or cold? In the sense do they execute on creation or wait to be invoked?

    Could it lead to problems if Task are cold in .NET and/or Python, while in JavaScript promise are hot ?

    While we decide on what to do here, a possible workaround for people who need to add cross platform asynchronous code is to create a module which encapsulate the compiler directives. Could benefit from helpers functions too.

    Note: Code below use async but I believe the same could be applied to Task

    module Main
    
    open Fable.Core
    
    // We need the `<'T>` here as it allows to mark the function/variable inline for better output.
    let inline crossAsync<'T> =
    #if FABLE_COMPILER
        promise
    #else
        async
    #endif
    
    type CrossAsync<'T> =
    #if FABLE_COMPILER
        JS.Promise<'T>
    #else
        Async<'T>
    #endif
    
    let hello (name : string) : CrossAsync<string> =
        crossAsync {
            return "Hello " + name
        }
    
    // Fake the API being called by the consumer code
    crossAsync {
        let! hello = hello "World"
        printfn "%s" hello
        return ()
    }
    #if FABLE_COMPILER
    |> Promise.start
    #else
    |> Async.Start
    #endif
  2. dbrattli commented on Dec 18, 2023

    @dbrattli
    CollaboratorAuthor

    Tasks in .NET are hot in the sense that when you hold a Task returned from an async method then it's already running regardless if you use await or not. So Task in .NET and Promises in JS are both hot. Tasks in Python are scheduled Futures so they are hot. This is also why I'm unsure about rewriting all Task based code to use F# async for interoperability.

    The workaround is nice if you own the codebase but e.g for Fable.Giraffe I want users to use their existing Giraffe webapp without having to modify their code. I.e they can decide if they want to run their web server on .NET, Python or Node (or a combination). Then it would be nice if we could call a Task for a Task.

  3. MangelMaxime commented on Dec 18, 2023

    @MangelMaxime
    Member

    Seems like in .NET there is an exception when you use the constructor to create the task.

    Tasks that are created by the public Task constructors are referred to as cold tasks... All other tasks begin their life cycle in a hot state.

    Source

    But this is something we can probably ignore (will need some testing so see how often this "problem" occurs).

    If I am reading you correctly, then we have:

    • .NET task are hot
    • Python scheduled futures are hot
    • JavaScript promise are hot
    • Rust ? @ncave What are your plans for supporting Task in Rust? Does Rust have hot task?

    If all the these major targets have the same hot behaviour available natively, I think it could makes sense to try implements an equivalence between them. This would allows, people to share the same code but also to use the native APIs.

    Currently, as you mentioned in your first message, we have these situations:

    Situation 1

    People prefers to use native primitives and so use Promise for JavaScript and Async for .Net.

    Then if they need to merge the code, they needs to use compilers directives or helpers like Async.AwaitPromise / Async.StartAsPromise

    Situation 2

    People value more shared code and write everything using Async which are natives to .NET but not to JavaScript for example.

    So implementing Task by making them use the native APIs would create a third situation which is the best of both world:

    Ability to use native F# API which is standard in F# so shareable and use native APIs in the runtime.

  4. ncave commented on Dec 18, 2023

    @ncave
    Collaborator

    @MangelMaxime

    What are your plans for supporting Task in Rust?

    There is already some partial support for Async/AsyncBuilder, Task/TaskBuilder, Thread/ThreadPool/Monitor added by @alexswan10k.

    Does Rust have hot task?

    Afaik Rust's futures are cold, but I'm not sure that is a showstopper.

    @dbrattli

    It's currently hard to use Fable to write code that uses Task

    Can you please elaborate, just trying to understand what the stated goal is.
    Is it to get most of the languages (JS/TS/Rust/Python) to support at least what is needed to pass the TestTask tests?

  5. dbrattli commented on Dec 18, 2023

    @dbrattli
    CollaboratorAuthor

    Yes, started porting Fable.Giraffe to TS/JS. It already runs on Python so the current Task tests would solve my needs. Could we make Giraffe run on Rust? 🙈

  6. ncave commented on Dec 18, 2023

    @ncave
    Collaborator

    @dbrattli

    Could we make Giraffe run on Rust?

    I guess the first step would be to make the TestTask tests pass.

  7. MangelMaxime commented on Jun 29, 2026

    @MangelMaxime
    Member

    The original issue has been fixed in #4694

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions