Solved

Race condition between event and new page load? (JavaScript SDK)


Badge

Let’s say I trigger an event when a submit button is clicked, isn’t there a race condition between sending that event and the browser to unload the current page in order to navigate to the new one? In other words, it could happen that the form is submitted and the new page loaded before the event is completely sent, so it gets lost?

Sorry in case I don’t see the forest for the trees here. It just seems like an obvious issue to me and I couldn’t find anything about it. I also couldn’t find any examples on how to code a button (or link) click listener, which would give me confidence. The examples are only about triggering the event itself.

Currently I see the following possibilities:

  1. The JavaScript SDK has some internal magic (with an offline queue or so) to resend events that may got lost in such situations.
  2. In practice the probability is almost 0 that the event gets overtaken by the new page load, so nobody cares about it.
  3. It’s a real issue and my idea of sending events on form submits (or even link clicks) is bad in the first place (and instead I should send them when the new page is loaded, or so).

I would appreciate any hints on this. Thanks in advance,

Sebastian

icon

Best answer by Yuanyuan Zhang 1 June 2022, 20:41

View original

7 replies

Userlevel 5
Badge +5

Hi @Sebastian

This is Yuanyuan from the Amplitude Support team - thank you for writing this post!

I will clarify this topic with our engineering tam and get back to you!

Best,


 

Userlevel 5
Badge +5

Hi @Sebastian

Our engineering team offers this solution for the situations you described: Using sendBeacon only when exiting page

For this situation you can do:

const onExitPage = amplitude.getInstance().sendEvents();
amplitude.getInstance().init(API_KEY, USER_ID, {
onExitPage: onExitPage,
});

This helps you by calling sendEvents() and switching to use sendBeacon API right before the page exits to minimize the number of unsent events, if not entirely prevent them.

Does this help? Please do not hesitate to reach out if you have any other questions!

Kind Regards,

Badge

Thanks @Yuanyuan Zhang. This makes sense and I understand that this may be better than setting the beacon transport mode for everything, in order to avoid data loss, for example in mobile settings.

But just to be sure:

  1. logEvent() is an asynchronous method in http transport mode, too, right? It will not block until the event submission was successful but return immediately?
  2. And sendEvents() will not switch to beacon mode permanently but only for the currently queued events that are about to be sent, correct? After that it will be http mode again?

(By the way I didn’t see sendEvents() in the API Reference).

Badge

@Yuanyuan Zhang unfortunately I get the following error if I initialize as you advised:

[Amplitude] Invalid onExitPage option input type. Expected function but received undefined

And indeed amplitude.getInstance().sendEvents() returns undefined, while we are actually looking for a callback function if I’m not mistaken. Please advise.

Thanks in advance.

Userlevel 5
Badge +5

Hi @Sebastian

Thanks for trying it!

For your follow-up questions:

  1. yes, logEvent() is asynchronous
  1. sendEvents() itself does not switch to the mode to beacon. It is the onExitPage optional callback that switches to beacon temporarily. onExitPage is only called right before page unloads.

For the “undefined” error, let me consult with the engineering team and get back to you. 

Best,

Userlevel 5
Badge +5

Hi @Sebastian,

You have tried with onExitPage: amplitude.getInstance().sendEvents, correct? Could you please also try onExitPage: () => amplitude.getInstance().sendEvents()?  Thanks.

Best,

Badge

Thanks for your answers @Yuanyuan Zhang. This makes sense and I can initialize it if I wrap it in a function as you wrote. Much appreciated.

Reply