Synchronous Platform.worker

Hi, is there a way to use Platform.worker (or some alternative) to make a synchronous calls from javascript to Elm and back?

It would be useful for porting our js code if pure elm functions that don’t produce any tasks would be callable from js.

1 Like

I have been looking into this today, and I think I can see how it is done, but I am not sure if it will be synchronous. I wrote “hello world” Platform.worker style:

Example

main : Program String String Never
main =
    Platform.worker
        { init = \flags -> ( "Hello " ++ flags, Cmd.none )
        , update = \msg model -> ( model, Cmd.none )
        , subscriptions = \model -> Sub.none
        }

Structure of the Javascript

Looking at the generated code, it is structure is as below (I’ve added some whitespace to try and show better how it is structured):

(function(scope)
{

  ...
  function _Platform_export(exports)
  {
  	scope['Elm']
		? _Platform_mergeExportsProd(scope['Elm'], exports)
		: scope['Elm'] = exports;
  }

  ...
  var author$project$Main$main = elm$core$Platform$worker(
  {
      ....
  });

  _Platform_export({'Main':{'init':author$project$Main$main(elm$json$Json$Decode$string)(0)}});
 
} (this) );

In summary, an anonymous function is immediately called, and an internal function within that sets the value of Main in the Elm scope, this has a field called init which is the worker. None of the functions you wrote are exposed outside of this big anonymous function that is wrapped around everything.

Passing args in: Running the Worker

You can run the worker like this (https://guide.elm-lang.org/interop/flags.html):

var app = Elm.Main.init({
  flags: "ondrej"
});

But there does not appear to be a way to get hold of the model from inside the Elm program?

Getting data out: ports?

You set up ports on the worker like this (https://guide.elm-lang.org/interop/ports.html):

In the Elm program:

port sayHello : String -> Cmd msg

main : Program String String Never
main =
    Platform.worker
        { init = \flags -> ( "Hello " ++ flags, sayHello <| "Hello " ++ flags)
        , update = \msg model -> ( model, Cmd.none )
        , subscriptions = \model -> Sub.none
        }

In javascript:

function greet(name) {
  var greeting;

  var app = Elm.Main.init({
    flags: name
  });

  app.ports.subscribe(function(data) {
    greeting = data;
  });

  return greeting;
}

greet('ondrej');

I am not sure though, that the first command returned from init will be processed synchronously to make this work?

1 Like

It looks like it doesn’t work with Platform.worker https://ellie-app.com/3svkC5KgR6na1 but with Browser.element it does https://ellie-app.com/3svnmFcvNRXa1.

I’m not sure exactly why :thinking:

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.