Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Promise / Callback Types (Instead of async/await?) #146

Open
caesay opened this issue Feb 20, 2024 · 3 comments
Open

Promise / Callback Types (Instead of async/await?) #146

caesay opened this issue Feb 20, 2024 · 3 comments
Labels
enhancement New feature or request

Comments

@caesay
Copy link
Contributor

caesay commented Feb 20, 2024

I think this depends on #144.

It's a very common library paradigm to provide asynchronous functions. In JS, it's not even possible to wait synchronously on a promise, so if directly using an asynchronous API in a native { } block, this can not be propagated up to a consumer of the library. It's also not possible to start a new thread to run a synchronous function in the background - so it's particularly important in that environment.

I suggest a simple implementation (when compared with an async/await state machine).

Also I'll note, #144 is a good step in the right direction, but falls short because it doesn't allow the consumer to easily block synchronously on the result.

The first step would be allowing the Task<T> type to be returned from methods. Translated into language-specific "Promise" types.

The second step would be some way to create and complete tasks:

class Task<T>
{
    public void Then(Action<T> fn);    
    public void Error(Action<TErr> fn);
}

class TaskFactory<T>
{
    public Task<T> AsTask();
    public void SetResult(T result);
    public void SetCancelled();
    public void SetException(Exception e);
}

The proposed API would allow Fusion authors to fully utilise and expose native asynchronous functionality initially, and later on, presumably, the Fusion standard lib will also expose some asynchronous functions.

Some examples:

public Task<string> GetStringFromUrl(string url)
{
    Task<string> ret; // this is required or fusion thinks the method doesn't return.
    native
    {
        var http = new System.Net.Http.HttpClient();
        ret = http.GetStringAsync(url);
    }
    return ret;
}

public Task<string> GetProcessOutputAsync(string path)
{
    TaskFactory<string> source;
    native
    {
        var psi = new System.Diagnostics.ProcessStartInfo()
        {
            CreateNoWindow = true,
            FileName = path,
            RedirectStandardError = true,
            RedirectStandardOutput = true,
            UseShellExecute = false,
        };
        
        System.Text.StringBuilder output = new System.Text.StringBuilder();
        
        var process = new System.Diagnostics.Process();
        process.StartInfo = psi;
        process.ErrorDataReceived += (sender, e) =>
        {
            if (e.Data != null) output.AppendLine(e.Data);
        };
        process.OutputDataReceived += (sender, e) =>
        {
            if (e.Data != null) output.AppendLine(e.Data);
        };
        
        process.Start();
        process.BeginErrorReadLine();
        process.BeginOutputReadLine();
        process.WaitForExit();
        
        process.WaitForExitAsync().ContinueWith(t =>
        {
            if (t.IsFaulted) source.SetException(t.Exception);
            else if (process.ExitCode != 0) source.SetException(new System.Exception($"Process exited with code {process.ExitCode}"));
            else source.SetResult(output.ToString());
        });
    }
    return source.AsTask();
}
@pfusik pfusik added the enhancement New feature or request label Feb 20, 2024
@pfusik
Copy link
Collaborator

pfusik commented Feb 20, 2024

I have very limited experience writing async code. I know Node.js heavily relies on it, but is it also used in browser JS ?

It took C# a decade to incorporate it and Java still doesn't have async/await.
Your code not only shows delegates, but also lambda functions. The problem with the latter is that C still doesn't support them.

@caesay
Copy link
Contributor Author

caesay commented Feb 21, 2024

I'm not suggesting we do async/await. I'm suggesting we support asynchronous callbacks (eg. Promise, Future, Task) - which predate async/await style keywords significantly. Java does have similar concepts (CompletableFuture) and C++ too (std::future)

In JS/TS, yes of course - async callbacks is even more important in the browser. Asynchronous code in the browser dates back to the very beginning of javascript. Pretty much everything you did had to be interrupted and continued at some point later with a callback (think waiting for user input, waiting for an HTTP request to complete, waiting for an animation to play, etc etc etc). Promise was implemented at some point later as an abstraction over the top of pure callbacks to simplify returning success/failure statuses and also chaining promises together (.then() and .error() is similar C# .ContinueWith()). It is not possible to use JS today without significantly relying on callbacks and promises, because many (especially browser functions) only provide asynchronous API's - anything synchronous would block the user input and freeze up the page since JS is single threaded. There is no way to synchronously wait on an asynchronous function for this reason too - to avoid deadlocks and UI freezes. There are some synchronous functions offered in the nodejs context, however using them is not advisable for the same reason, imagine you're writing a HTTP server, but the moment you do a file copy you stop handling requests because you're synchronously blocking your only thread doing some simple IO....

In other languages, it's probably not quite as important (in JS it's an absolute requirement as a library author). However every language will have a similar concept to a Promise, and if it doesn't, a simple abstraction can be added on top of pure callbacks, which is why I suggest that this depends on #144.

The reasons above are also why when we start writing API's like "CopyFile", "HttpRequest" and so forth, we need to support and expose asynchronous functions in Fusion... otherwise it will never be possible to use Fusion as a library for JS in a practical sense because of it's singlethreaded nature.

@pfusik
Copy link
Collaborator

pfusik commented Feb 21, 2024

Thank you!

Last time I did any significant browser JS was a dozen years ago: validation, DOM manipulations and AJAX. No Promises. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants