Mono: Blazor Client Side Preview 7: Doing http calls in OnInitAsync throws in iOS

Created on 2 Aug 2019  路  40Comments  路  Source: mono/mono

_From @GerritBergen on Thursday, August 1, 2019 1:36:07 PM_

Describe the bug

Running any http requests in OnInitAsync on the index page will result in iOS console throwing error

Unhandled Promise Rejection: RangeError: Maximum call stack size exceeded (blazor.webassembly.js:1:32190)

After this error the application will not load.

To Reproduce

Steps to reproduce the behavior:

  1. Take the template project that has client/server/shared in seperate projects
  2. Put all the code from FetchData.razor into Index.razor (and change the @ page back to "/")
  3. Run from an iOS device (iOS 12.4 Safari)

Additional context

.NET Core SDK (reflecting any global.json):
Version: 3.0.100-preview7-012821
Commit: 6348f1068a

Runtime Environment:
OS Name: Windows
OS Version: 10.0.18362
OS Platform: Windows
RID: win10-x64
Base Path: C:\Program Files\dotnet\sdk\3.0.100-preview7-012821\

Host (useful for support):
Version: 3.0.0-preview7-27912-14
Commit: 4da6ee6450

.NET Core SDKs installed:
2.1.700 [C:\Program Files\dotnet\sdk]
2.1.701 [C:\Program Files\dotnet\sdk]
2.1.800-preview-009696 [C:\Program Files\dotnet\sdk]
2.1.800 [C:\Program Files\dotnet\sdk]
2.1.801 [C:\Program Files\dotnet\sdk]
3.0.100-preview7-012821 [C:\Program Files\dotnet\sdk]

.NET Core runtimes installed:
Microsoft.AspNetCore.All 2.1.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
Microsoft.AspNetCore.All 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
Microsoft.AspNetCore.App 2.1.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 3.0.0-preview7.19365.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.NETCore.App 2.1.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 3.0.0-preview7-27912-14 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.WindowsDesktop.App 3.0.0-preview7-27912-14 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

_Copied from original issue: aspnet/AspNetCore#12799_

Most helpful comment

Hey everyone. I am glad that it is working for everyone so far. We hope to have a more definitive answer soon. Thank you all for your patience while we are working through this.

All 40 comments

Adding a 4 second delay before doing any network calls is my temporary workaround for now. Although not ideal, at least I can keep working.

I would add a further clarification. This is only true (in my experience) if the page being loaded is as part of the start up of the app. In other words, Http calls in OnInitAsync are fine if the page is navigated to from within the app. (i.e. UriHelper.NavigateTo(...) but if the page is navigated to from an external link or if the user hits the brewers refresh button, then it's game over.

And finally I'm confident this was not an issue with Preview 6

I can confirm. Works in preview 6.

Adding a 4 second delay before doing any network calls is my temporary workaround for now. Although not ideal, at least I can keep working.

Doesn't work in my case.

I can confirm this is still an issue after upgrading to preview 8

Was really hoping it would be fixed. :/ I wonder when we'll get some insight from the devs

I wonder if this case is in the right spot --- I guess I don't understand why it's a mono thing and not an aspnet thing (who owns Blazor?). More importantly, I wonder if anybody realizes that this newly introduced bug makes going live impossible.

@rshillington it was over in the aspnetcore repo but @mkArtakMSFT moved it here. It seems like a bug with the mono runtime, which blazor uses. But it's been 2 weeks, I was hoping by now we'd have some dev response. At least acknowledging it's an issue.

/ping @richlander

I have opened a bug report here: https://bugs.webkit.org/show_bug.cgi?id=201026

Hopefully they can provide a little assistance.

It appears I'm having the same issue with emcc (see https://github.com/emscripten-core/emscripten/issues/6042#issuecomment-524033171)

For my part it only happen on iOS 13 (tested with iPhone 7, Xs Max and iPad Pro), I hope that Apple can fix this regression in iOS 13 before its release.

I guess the first thing we need to know is if this is caused by the number of calls or is it the call stack memory that is causing it. The error that I am seeing hints to the number of calls on the stack whereas Unhandled Promise Rejection: Error: Out of executable memory in function at index seems to hint at the stack memory itself is being exhausted.

In one of the regressions I filed for safari desktop and WebWorkers the stack limit was always hit at 50 calls. The funny thing is that the same project works on mobile safari.

Actually, i'm having the same issue on:

  • iOS 12.4 - iPhone 6
  • iOS 12.4 - iPad 9.7" Retina (6th generation)

on my BlazorMobile project.

My scenario is a little different. I'm waiting for the index page to render, and then a delegate callback my code.

I'm doing some local network operation on a websocket to retrieve device information, like does it have an accessToken cached.

Everything is ok, even the local network call.
But if from this place, i call NavigateTo, to render either the login page or my app home page, i encounter this error.

As stated somewhere, adding a 4 seconds delay before executing the custom code does not fire this bug.

I noticed that the delay to add was somehow variant. It works with 2 seconds on iPad, but not with the iPhone 6. But 4 seconds seem ok for both.

Daddon do you have an example of that somewhere live?

No sorry, it's from a Business app i'm currently developping to a client.

I was not able to reproduce this behavior on my samples projects. Even if i was doing some "things" and then call NavigateTo, i was not having exception.

Maybe the workload / number of past asynchronous operations before trying to navigate has an impact on this bug.

@kjpou1 For example, if you view the this client side Blazor app: https://ui.playersmatter.com and then navigate to the about page using the sidebar menu everything will work fine. However if you navigate directly to the address https://ui.playersmatter.com/about on an iOS 12.4 device the app fails to load (and using viewing the console output you'll observe the error)

the About page does a simple call to an api server to get the api version. This call is made in the OnInitAsync method which works when navigating internally, but fails if navigating directly.

Hop that helps.

@rshillington this is the exact scenario. It can even be reproduced with the blazor templates without changing a thing.

@kjpou1 just wondering if you know how long it usually takes the webkit guys to respond.

Anyone here find any other workaround besides waiting 4 seconds?

I can say that the 4 second wait, is not a reliable solution!

@rshillington This is not a solution, but did you have any problems with the 4 seconds ?

@rshillington @Daddoon it seems to work reliably on my app (with 5 different iOS devices), but yeah, definitely not a solution. Better than nothing I suppose but I really hope they fix this soon.

I'm wondering if it belongs to the webkit or mono team. This worked with preview 6, and only failed as soon as I updated the preview, with no changes to the iOS version. :/

@GerritBergen Thanks for your feedback ! This is less a problem for me as i mainly use Blazor as a mobile application with my plugin. So i can just placeholder all the waiting time with the app logo in background on the start page.

In my opinion, as the bug occur only on iOS / Webkit, i'm pretty sure that this is a "bug" (sort of) of Webkit. I think that Blazor (that use mono-wasm -> so mono team !) is just in a buggy edge case that happen only in Webkit webassembly. Maybe the bug was not present before because some underlying implementation were differents.

I read the callstack when everything throw some days ago. Even if everything is minified, from the callstack, it seem that it's doing some wasm code, then call setTimeout (don't remember if it's one or twice), and then the callstack error throw and so bubble up in the mono.js driver.

Does someone tested if, as a workaround, it throw if the ending page is loaded, and that the call that was expected (with the network call) is delayed to fire (with a timer or setTimeout) ?

Cross fingers, as according to Microsoft roadmap (correct me if i'm wrong), Blazor server-side should not be preview anymore in September.

I think we can expect more feedback on WASM after .NET Core 3.0 RTM release ?

@Daddoon I'm doing the same thing. I check if the browser is mobile safari using the user agent, and continue displaying the splash screen until the timeout ends.

Nothing yet but we are looking into it. How about wrapping the async call in a Task.Run (async () => { }); to see if that works as a stop gap for now.

   protected override async Task OnParametersSetAsync()
    {
        startDate = StartDate.GetValueOrDefault(DateTime.Now);
        await Task.Run (async () => {
            forecasts = await Http.GetJsonAsync<WeatherForecast[]>(
                $"sample-data/weather.json?date={startDate.ToString("yyyy-MM-dd")}");

            // Because StandaloneApp doesn't really have a server endpoint to get dynamic data from,
            // fake the DateFormatted values here. This would not apply in a real app.
            for (var i = 0; i < forecasts.Length; i++)
            {
                forecasts[i].DateFormatted = startDate.AddDays(i).ToShortDateString();
            }
        });
    }

@kjpou1 Your suggestion seems to have done the trick. I updated my demo site test as described above and can can't reproduce the behaviour.

So now I'm curious, why would this work?

@kjpou1 Well, i always assumed that i was not able to use any Task.Run method on Blazor because wasm does not support to execute multiple thread simultaneously.

Does this work because the Task.Run is awaited (and the only other one) and so there is only one thread at a time working as the main thread is waiting ?

EDIT: Nevermind, WebAssembly seem to support multi-threading out of the box, because of the usage of ServiceWorker, even if it seem that there is something not consistent between browsers about the implementation of SharedBufferArray (or something like that).

I think i assumed this was only one thread possible because i was not aware of this info and because previously, Blazor was also supported on asm.js, wich rely on Javascript, wich only have one thread and no service workers as far as i know on older browsers.

@rshillington I'm too curious. From my own, i would think this is because the entire Task and continuation context is delegated to an another call stack because of the use of Task.Run, and because of that, maybe this woraround the callstack limitation/leak, or at least, even if it's buggy, this only remain on the newly created task, and so avoiding the crash on the main one.

Hey everyone. I am glad that it is working for everyone so far. We hope to have a more definitive answer soon. Thank you all for your patience while we are working through this.

@kjpou1 Not yet tested because on vacation, but i'm confident with the feedback. A workaround is better than waiting.

Thanks a lot @kjpou1 !

Thanks @kjpou1 for the workaround! This looks very promising. :)

@kjpou1, in case it matters, the workaround did not work for me. Now, instead of hanging on the download screen, the application actually displays the content of the index page but then it hangs there.

I don't own a mac so I am not able to troubleshoot, I have no idea what error message the app is throwing. This only fails on iOS (iPhone and iPad).

@rxelizondo Did you write the await keyword ?

@Daddoon, yes of course, I essentially wrapped the previous code inside the Task.Run call.

await Task.Run(async () =>
{
     await CallPreviousCode();
});

Am I missing something?

@rxelizondo I think everything is right.

Hey everyone

Could someone out there rebuild their app but remove the following packages first so the packages get restored fresh. This is just a test.

microsoft.aspnetcore.blazor.mono
mono.webassembly.interop
microsoft.jsinterop

If it still works and you have the Task.Run as mentioned earlier in the code please remove it and see if it runs without it.

Again, this is just a test.

@kjpou1 I can't say that I did exactly what you asked, but I can say that I upgraded to Preview 9 and removed the Task.Run workaround, and things seem to be working.

@rshillington Nice to know ! I will test on my iPad and iPhone Monday and will give my feedback too :) !

I can confirm, i don't have this behavior anymore since preview9 on my iPhone, wich was the worst case.
No Task.Run, no delayed start, and everything is working right out of the box now.

Just wishing that iOS 13 will not have any WebAssembly regression!

This should be fixed by updating to AspNetCore Preview 9. Will close this for now and if need be open another issue against Preivew 9.

I still have this problem with Preview 9. In various places, adding an await Task.Delay() helped, but one other area is with constructor DI. If I have a class like this:

public class Settings
{
    public Settings(ILogger<Settings> log) => Log = log;
}

Then inject an instance like this:

@inject MyApp.Shared.Models.Settings Settings

It'll break Safari. If I remove that constructor, the injection will work (but Log will of course be null).

I can't really inject something after a delay, can I?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

zwcloud picture zwcloud  路  3Comments

triztian picture triztian  路  4Comments

equalent picture equalent  路  4Comments

DanielCauser picture DanielCauser  路  4Comments

equalent picture equalent  路  4Comments