Core: Ports do not register immediate `send`

Created on 11 May 2016  路  18Comments  路  Source: elm/core

I've produced an example here: https://github.com/knewter/counter

It's actually published here: http://knewter.github.io/counter/Counter.html

If you load the page, the counter will have value 0. The Counter.html file has the following code:

var app = Elm.Main.fullscreen();
app.ports.jsMsgs.send(1);
window.app = app;

I mention the last bit because you can open up the browser's console, issue app.ports.jsMsgs.send(1), and the counter will update. So the inbound port works, but it doesn't work immediately.

Most helpful comment

As a workaround I'm using something like:

setTimeout(function(){ app.ports.jsMsgs.send(1); }, 0)

That's not pretty but it does the job.

All 18 comments

I think this is the same bug as this one: https://github.com/elm-lang/core/issues/582

it would appear to be the same bug. This is a simpler case that produces the error though. I didn't realize they were the same error before.

As a workaround I'm using something like:

setTimeout(function(){ app.ports.jsMsgs.send(1); }, 0)

That's not pretty but it does the job.

Yep, just hit this in our production app :(. Thanks to @lukewestby for pointing us at this bug!

Same here, pretty annoying when discovering elm.
You often start by integrating it to your existing JS code with ports and you can encounter this bug pretty early in your development process.

@yoni-tock and I are going to pair on fixing this when he has some time in a couple weeks if no one can get to it earlier

Just letting you all know I've experienced this bug too:

https://www.reddit.com/r/elm/comments/4u3icn/incoming_port_not_working/

I've been looking into the issue. So far, I can tell that the app gets initialized using the scheduler (which doesn't do any work until the next turn of the event loop). I imagine three possible fixes:

  • Add a way to schedule work immediately (to ensure the app is initialized in the same turn as fullscreen/embed/worker)
  • Rearrange the work queue on the on the first loop so that the scheduler runs initialization first
  • Make initialization synchronous (which may mean bypassing the scheduler altogether, but that may cause other things to break)

What if the initialization functions accepted a callback instead of returning the application object synchronously?

Elm.Main.fullscreen(flags, function (app) {
  app.ports.whatever.send('a string')
})

@lukewestby, I don't think we should make the API more complex. It's just a bug. It's totally possible to queue messages while things are initializing. In fact, the way I implemented the scheduler, that is exactly what I expect to be happening already. I looked into this quite some time ago and could not figure it out.

If folks want to continue discussion on this thread, please focus on exploring and sharing technical details. Obviously more people will run into this as more time passes. We do not need to record that every time it happens. So learn about the scheduler. If there is something you are really struggling with, ask on elm-dev. There's also a timing bug in the scheduler that I could not figure out. I think the scheduler is one of the coolest parts of the platform, so I think it would be fun for folks to look into these things.

@evancz @lukewestby The initial messages are being queued. I see this sequence:

  • setupOutgoingPort and setupIncomingPort both execute
  • makeEmbedHelp executes (but in the process, enqueues the initialization code)
  • any sends on that same turn of the event loop are enqueued, but onEffects for those ports haven't run yet, thus subList is empty (effectively dropping the messages)

I'll jump on elm-dev with questions about the scheduler, but just wanted to leave more details here.

@artisonian, let @lukewestby and I know what we can do to help. We were planning to dive into this next week

@lukewestby and I just set up a SSCCE here

Candidate fix PR to core coming soon

Cool. How did you tackle it?

BTW, I'm thinking about writing a blog post or something that walks through the internals of the scheduler. Think it's worth it?

@artisonian this is what we did: https://github.com/elm-lang/core/pull/685

YES! Definitely write that blog post!

Closing this as elm-lang/core 4.0.5 was published yesterday containing the fix

I am using elm-lang/core 4.0.5 and it is not fully fixed.

In init I return a port Cmd, and the port is this:

app.ports.is_key_set.subscribe(function(key){
    app.ports.keyData.send([key, localStorage.getItem(key) == "true"])
})

If I do a Debug.log on subscriptions, it says subscribed on keyData before is_key_set is called in javascript side, yet update function is not called.

If I change it to:

app.ports.is_key_set.subscribe(function(key){
    setTimeout(function(){
        app.ports.keyData.send([key, localStorage.getItem(key) == "true"])
    }, 0)
})

update is called.

@amitu please open a new issue and provide enough code to constitute an SSCCE of the problem you're experiencing

Was this page helpful?
0 / 5 - 0 ratings