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

Order of port declarations influences behaviour #896

Closed
zwilias opened this issue Aug 10, 2017 · 3 comments
Closed

Order of port declarations influences behaviour #896

zwilias opened this issue Aug 10, 2017 · 3 comments
Labels

Comments

@zwilias
Copy link
Member

zwilias commented Aug 10, 2017

SSCCE

https://ellie-app.com/3YKLTxbsg7na1/1

TL;DR

  • in JS, a subscription which calls send
  • in Elm, an update which modifies the model to enable a subscription and triggers that outgoing port
  • send is called before the subscription handler is scheduled, hence dropping the message.

Problem statement

Setting state to enable a subcription and triggering an outgoing port which synchronously calls back into Elm at the same time leads to this response being missed. This can easily come up when using a subscription-structure like advocated by elm-spa-example.

This is surprising since the user has already configured their model so that a subscription should be enabled, and how/when those subscriptions actually go into effect is an implementation detail the user shouldn't need to worry about.

Possible fix

Ensuring that subscription handlers are scheduled before commands are scheduled seems like a fix; I'm not sure if there are potential adverse effects to doing so.

@process-bot
Copy link

Thanks for the issue! Make sure it satisfies this checklist. My human colleagues will appreciate it!

Here is what to expect next, and if anyone wants to comment, keep these things in mind.

@zwilias
Copy link
Member Author

zwilias commented Aug 10, 2017

Note that - and this is the truly unexpected part - flipping the order in which the ports are declared fixes this issue, since this directly corresponds to the order in the managers object.

https://ellie-app.com/3YKLTxbsg7na1/2 (only difference with /1 is flipping the order of ports)

Subs first means subscriptions will be processed first

port toElm : (String -> msg) -> Sub msg
port toJS : String -> Cmd msg

Subs last means subs will be processed last.

port toJS : String -> Cmd msg
port toElm : (String -> msg) -> Sub msg

The order in which ports are declared really should not impact behaviour.

@zwilias zwilias changed the title Synchronous ports may lead to missed values Order of port declarations influences behaviour Aug 10, 2017
@evancz evancz added the problem label Aug 10, 2017
@evancz
Copy link
Member

evancz commented Dec 5, 2019

Thank you for the SSCCE! It looks like the fix for conditional subscriptions in 704dcc0 works for this case as well.

I updated the code for 0.19.1 and tried it with my local version of elm/core and it seems to be working as expected now. The fix should be available in 1.0.3 along with some other improvements.


Here is the updated SSCCE since I have it right now:

<html>
<head>
  <script src="elm.js"></script>
</head>
<body>
  <script>
    var app = Elm.Bug.init();
    app.ports.toJS.subscribe(function (value) {
      console.log("Received", value);
      app.ports.toElm.send(value);
    });
  </script>
</body>
</html>
port module Bug exposing (..)

import Browser
import Html exposing (..)
import Html.Events exposing (onClick)


type alias Model =
    { receiving : Bool, received : String }


type Msg
    = Enable
    | Receive String


port toJS : String -> Cmd msg


port toElm : (String -> msg) -> Sub msg


init : () -> ( Model, Cmd msg )
init _ =
    ( Model False "", Cmd.none )


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        Enable ->
            ( { model | receiving = True }, toJS "marker" )

        Receive val ->
            ( { model | received = val }, Cmd.none )


subscriptions : Model -> Sub Msg
subscriptions { receiving } =
    if receiving then
        Receive |> toElm
    else
        Sub.none


view : Model -> Browser.Document Msg
view { received } =
    { title = "Bug"
    , body =
        [ text description
        , br [] []
        , button [ onClick Enable ] [ text "send it and enable receiving" ]
        , br [] []
        , text <| "Received: " ++ received
        ]
    }


main : Program () Model Msg
main =
    Browser.document
        { init = init
        , update = update
        , view = view
        , subscriptions = subscriptions
        }


description : String
description =
    """
    Upon pressing the button, a message is sent to JS which is then synchronously dispatched back to Elm. Since subscription-handlers aren't scheduled strictly before sending out commands, this message is dropped.
    """

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

No branches or pull requests

3 participants