I end up add an exit function to every page with the help of added Global.Msg .
Then, let onUrlChange call the exit function but not entering new page immediately.
The exit function casts a Global.StepUrl msg after destruction completed which will finally enter new page.
Main.elm
-- UPDATE
case ( message, model.page ) of
( UrlChanged url, Login login ) ->
Login.exit url login |> updateWith Login LoginMsg model -- updateWith just do some Cmd.map
( GlobalMsg (Global.StepUrl url session), _ ) ->
stepUrl url session model -- Init new page
Page/Login.elm
exit : Url.Url -> Model -> ( Model, Cmd msg, Cmd Global.Msg )
exit url model =
( model, Cmd.none, (cast (Global.StepUrl url model.session)) )
-- Or we can just return ( model, destructCmd, Cmd.none) and cast Global.StepUrl after received destruction complete message.