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

Run jsdom shutdown on the next tick to avoid errors being thrown #16

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

mizzao
Copy link
Contributor

@mizzao mizzao commented Mar 6, 2021

This fixed #14 for me.

However, I think this deserves a bit more thought. It can lead to resolveHtml() getting called twice, for example.

@mizzao
Copy link
Contributor Author

mizzao commented Mar 6, 2021

Fixed it to make sure it's not called twice. But still might have some issues I overlooked...

@jakobrosenberg
Copy link
Member

This one's interesting. I like the fault tolerance it adds.

// Provide a tick for any queued jsdom operations to wrap up without error            
setTimeout(() => dom.window.close(), 0)

Maybe we should make it an opt-in/opt-out option.

Alternatively we could also catch all errors after dom.window.close() and print a small note a la: "path/to/xxx.svelte" produced an error after the window was closed. This is most likely caused by the window closing before all async code has been resolved. Please verify the content of "/path/to/xxx.html". This message can be disabled by setting "tossr.strict" to false.

@mizzao
Copy link
Contributor Author

mizzao commented Mar 8, 2021

So the reason I was getting this error is basically that I was calling $ready() as soon as remote data was loaded. This appears to have caused a race condition between:

  • The document being serialized
  • Svelte operations to update the DOM

As you observed, the error seems to have occurred because the page was serialized and dom.window.close() called before Svelte had a chance to finish "doing its thing". So, 'twas user error, but still not a reason to fail this way, as valid HTML was returned.

On your suggestion: I'm not sure how you would be able to catch errors after dom.window.close() given how they are intercepted. This would work in a one-shot production serverless function, but in dev mode how would you know which rendering operation caused the error, given that you are just looking for any unhandled async rejection?

Also note that without the guard to call resolveHtml() only once, it was actually getting called twice during this race, as evidenced by that console.log message getting printed out twice. Perhaps the closing of the window caused $ready() to fire a second time.

Anyway, I'm using this code on Netlify and it seems to have gotten rid of all the silent failures, so I'll let you decide what to do with this. (Netlify doesn't like an error being thrown after the HTML is generated: if it hasn't sent the response back yet, it'll do something weird as in roxiness/routify-starter#96)

@mizzao mizzao marked this pull request as ready for review March 8, 2021 22:49
@jakobrosenberg
Copy link
Member

I was calling $ready() as soon as remote data was loaded

This should be perfectly fine. Any chance there's an async call running after the data is loaded?

I'm puzzled by resolveHtml being called twice. The three calls should be mutually exclusive. That is except for the event where $ready() is called after tossr has timed out.

removeEventListener should fix that.

if (dom.window._document) {
  console.log(`[tossr] ${url} Waited for the event "${eventName}", but timed out after ${timeout} ms.`);
  resolveHtml()
  // guard against a second call
  dom.window.removeEventListener(eventName, resolveHtml) //added
}

I will probably have to bite the bullet and write some more extensive tests for tossr soon.

@mizzao
Copy link
Contributor Author

mizzao commented Mar 10, 2021

Any chance there's an async call running after the data is loaded?

I'm puzzled by resolveHtml being called twice. The three calls should be mutually exclusive. That is except for the event where $ready() is called after tossr has timed out.

This might be the same problem. Some async code runs after load, fires the app-loaded event again, and resolveHtml() is called a second time before dom.window.close().

Debugging this further seems above my pay grade. Though I'm using these two commits in my deployment so far, and I haven't see any runtime errors or double-calls. I'll check back in here if I notice anything else.

@jakobrosenberg
Copy link
Member

Much appreciated! @mizzao

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

Successfully merging this pull request may close these issues.

Svelte errors from jsdom attempting to run code after tossr promise returns
2 participants