<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Thoma Wang</title>
    <description>The latest articles on DEV Community by Thoma Wang (@xg).</description>
    <link>https://dev.to/xg</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F87911%2F25bb803f-9daf-43fa-8a17-44077ec82c8a.jpeg</url>
      <title>DEV Community: Thoma Wang</title>
      <link>https://dev.to/xg</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/xg"/>
    <language>en</language>
    <item>
      <title>Book review: Atomic Habits</title>
      <dc:creator>Thoma Wang</dc:creator>
      <pubDate>Thu, 11 Feb 2021 00:08:41 +0000</pubDate>
      <link>https://dev.to/xg/book-review-atomic-habits-j7h</link>
      <guid>https://dev.to/xg/book-review-atomic-habits-j7h</guid>
      <description>&lt;p&gt;The book is a very practical book, it has very detailed steps to help you build habits &amp;amp; break bad ones.&lt;/p&gt;

&lt;p&gt;The author kindly puts the cheat sheet online: &lt;a href="https://s3.amazonaws.com/jamesclear/Atomic+Habits/Habits+Cheat+Sheet.pdf"&gt;https://s3.amazonaws.com/jamesclear/Atomic+Habits/Habits+Cheat+Sheet.pdf&lt;/a&gt;&lt;br&gt;
The cheat sheet categorized the steps in his four-step model of habits - cue, craving, response, and reward - and the four laws of behavior change that evolve out of these steps.&lt;/p&gt;

&lt;p&gt;Most tips in this book I've already been doing, but it helps me to develop a framework to understand the whole flow a bit deeper.&lt;/p&gt;

&lt;p&gt;That said, I think the four-step model is better for someone who's already known what habits to build and what to break.&lt;br&gt;
Many times my struggle is not knowing what is a better habit for my own goal, therefore not convinced enough to invest in such habits - once I know a habit is benefiting me, I can stick with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Identity change is the key
&lt;/h2&gt;

&lt;p&gt;My biggest take-away from the book is not the concrete steps/instructions, it is the habit-identity shaping theory.&lt;/p&gt;

&lt;p&gt;Some highlights from the section:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How Your Habits Shape Your Identity (and Vice Versa):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Changing our habits is challenging for two reasons:

&lt;ol&gt;
&lt;li&gt;We try to change the wrong thing and&lt;/li&gt;
&lt;li&gt;We try to change our habits in the wrong way.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;True behavior change is identity change. You might start a habit because of motivation, but the only reason you’ll stick with one is that it becomes part of your identity.&lt;/li&gt;
&lt;li&gt;Progress requires unlearning. Becoming the best version of yourself requires you to continuously edit your beliefs, and to upgrade and expand your identity.&lt;/li&gt;
&lt;li&gt;Decide the type of person you want to be. Prove it to yourself with small wins.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;The mindset change is from: &lt;code&gt;Outcomes -&amp;gt; Processes -&amp;gt; Identity&lt;/code&gt; to &lt;code&gt;Identity -&amp;gt; Processes -&amp;gt; Outcomes&lt;/code&gt;.&lt;br&gt;
In other words, you don't become a certain type of person because you've done something or established a routine; you naturally understand what routine to have and generate outcomes because you've become a certain type of person, or have started to believe in yourself to be.&lt;/p&gt;

&lt;p&gt;For example, my goal is to become a reader. I don't "become" a reader when I check the daily reading habit for 1 year or something.&lt;br&gt;
I should understand that a reader should try to read books more, ideally everyday when I decide to be a reader.&lt;br&gt;
I would also start to think about what else would a reader do that I'm not currently doing - and I should start doing those because I'm a reader!&lt;/p&gt;

&lt;p&gt;Back to my struggle of "unknown what habits to build", one possible solution is to ask folks who have the identity I seek to have.&lt;br&gt;
I can ask mentors and friends of mine who read a lot about what habits they have and what their daily routines are.&lt;br&gt;
I don't need to follow all of them, but I can get a better understanding of the commonalities and become a better self.&lt;/p&gt;

&lt;p&gt;Start from the identity change, then true habits emerge.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four laws for habits building
&lt;/h2&gt;

&lt;p&gt;Source: &lt;a href="https://s3.amazonaws.com/jamesclear/Atomic+Habits/Habits+Cheat+Sheet.pdf"&gt;https://s3.amazonaws.com/jamesclear/Atomic+Habits/Habits+Cheat+Sheet.pdf&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Creating good habits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The 1st Law: Make It Obvious&lt;/li&gt;
&lt;li&gt;The 2nd Law: Make It Attractive&lt;/li&gt;
&lt;li&gt;The 3rd Law: Make It Easy&lt;/li&gt;
&lt;li&gt;The 4th Law: Make It Satisfying&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Breaking bad habits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Inversion of the 1st Law: Make It Invisible&lt;/li&gt;
&lt;li&gt;Inversion of the 2st Law: Make It Unattractive&lt;/li&gt;
&lt;li&gt;Inversion of the 3rd Law: Make It Difficult&lt;/li&gt;
&lt;li&gt;Inversion of the 4th Law: Make It Unsatisfying&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>book</category>
      <category>productivity</category>
    </item>
    <item>
      <title>You May Not Know Beacon</title>
      <dc:creator>Thoma Wang</dc:creator>
      <pubDate>Wed, 20 Jan 2021 06:15:26 +0000</pubDate>
      <link>https://dev.to/xg/you-may-not-know-beacon-9ba</link>
      <guid>https://dev.to/xg/you-may-not-know-beacon-9ba</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;navigator.sendBeacon&lt;/code&gt; should be used with &lt;code&gt;visibilitychange&lt;/code&gt; and &lt;code&gt;beforeunload&lt;/code&gt; events, otherwise you'll lose data&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;navigator.sendBeacon&lt;/code&gt; has in-flight data limit and other constraints&lt;/li&gt;
&lt;li&gt;Use Beacon API with proper fallback to reliably beacon data to server&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is Beacon
&lt;/h2&gt;

&lt;p&gt;From &lt;a href="https://www.w3.org/TR/beacon/" rel="noopener noreferrer"&gt;W3C spec Beacon&lt;/a&gt;, Beacon is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;an interface that web developers can use to schedule asynchronous and non-blocking delivery of data that minimizes resource contention with other time-critical operations, while ensuring that such requests are still processed and delivered to destination.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;em&gt;delivery of data&lt;/em&gt; is just an abstract way of saying the browser makes an HTTP request that sends back data to the server. The reason for another API that does HTTP when we already have &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest" rel="noopener noreferrer"&gt;XMLHttpRequest&lt;/a&gt; and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API" rel="noopener noreferrer"&gt;Fetch API&lt;/a&gt;, is to address a typical challenge web developers have.&lt;/p&gt;

&lt;p&gt;There are some HTTP requests from the browser that don't need to read or even wait for the server response, usually event tracking, status update, and analytics data. The characteristics of these type of requests are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No need to access HTTP response - send and forget&lt;/li&gt;
&lt;li&gt;Lightweight - should not impact the user experience or taking too much network bandwidth&lt;/li&gt;
&lt;li&gt;Happen in the background without user interaction&lt;/li&gt;
&lt;li&gt;Need to &lt;strong&gt;reliably send when closing the page&lt;/strong&gt; AKA, page unload&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keeping these in mind, the above description of the goals of Beacon API would make more sense.&lt;/p&gt;

&lt;p&gt;The explicit goals of the Beacon API are to provide a minimal interface to web developers to specify the data and endpoint, then let the browser coalesces the requests.&lt;/p&gt;

&lt;p&gt;Because Beacons do not provide response access in a fire-and-forget way and coalesced by the browser, the browser guarantees to initiate these data delivery requests before page is closed/unloaded, and outlive the page lifecycle.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Use
&lt;/h2&gt;

&lt;p&gt;You can use beacon via &lt;code&gt;navigator.sendBeacon()&lt;/code&gt;. A minimal example is given from the W3C spec:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="c1"&gt;// emit non-blocking beacon to record client-side event&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;reportEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendBeacon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/collector&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// emit non-blocking beacon with session analytics as the page&lt;/span&gt;
  &lt;span class="c1"&gt;// transitions to background state (Page Visibility API)&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;visibilitychange&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;visiblityState&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hidden&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;sessionData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;buildSessionReport&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendBeacon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/collector&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sessionData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
 &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;'http://www.w3.org/'&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;'reportEvent(this)'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"reportEvent('some event')"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Click me&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MDN has the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon" rel="noopener noreferrer"&gt;complete API documentation&lt;/a&gt;, go take a look!&lt;/p&gt;

&lt;h2&gt;
  
  
  Alternatives
&lt;/h2&gt;

&lt;p&gt;People have used alternative ways to do what Beacon API meant to do.&lt;br&gt;
By using &lt;code&gt;XMLHttpRequest&lt;/code&gt; or &lt;code&gt;fetch&lt;/code&gt;, you can POST data periodically in the background, and it's totally fine not to read the response.&lt;/p&gt;

&lt;p&gt;Another way is to create an &lt;code&gt;img&lt;/code&gt; element and leverages the fact it makes a GET request to server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://mysite.com?&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem is when the user closes the page, the last request is killed and there's no way to recover. In other words, a significant amount of your analytics data is lost and causes data distortion.&lt;/p&gt;

&lt;p&gt;To avoid the closing page problem, a solution is to create a &lt;code&gt;sync&lt;/code&gt; XHR on &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event" rel="noopener noreferrer"&gt;&lt;code&gt;beforeunload&lt;/code&gt;&lt;/a&gt; or &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/unload_event" rel="noopener noreferrer"&gt;&lt;code&gt;unload&lt;/code&gt;&lt;/a&gt; events, this is very bad for user experience as it blocks the page unloading - imagine your customers have to wait a noticeable amount of time to close the browser tab.&lt;/p&gt;

&lt;p&gt;In fact, &lt;code&gt;beforeunload&lt;/code&gt; and &lt;code&gt;unload&lt;/code&gt; are explicitly said to be legacy API and should be avoided. See &lt;a href="https://developers.google.com/web/updates/2018/07/page-lifecycle-api#legacy-lifecycle-apis-to-avoid" rel="noopener noreferrer"&gt;Page Lifecycle API &amp;gt; Legacy lifecycle APIs to avoid&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Confusion
&lt;/h2&gt;

&lt;p&gt;It seems easy, a simpler API that does the work reliably. However, people have had issues in production and not seeing the data being beaconed back as expected. &lt;a href="https://volument.com/blog/sendbeacon-is-broken" rel="noopener noreferrer"&gt;Beacon API is broken&lt;/a&gt; post described their experiment setup and the results suggest Beacon API is not working as expected.&lt;/p&gt;

&lt;p&gt;Reading through the comments section, the problem becomes clear that Beacon itself never had any issues, it is when to call the API.&lt;/p&gt;

&lt;p&gt;MDN added you should use &lt;code&gt;sendBeacon&lt;/code&gt; with &lt;code&gt;visibilitychagne&lt;/code&gt;, not &lt;code&gt;unload&lt;/code&gt; or &lt;code&gt;beforeunload&lt;/code&gt;, after the comment discussions from the &lt;a href="https://volument.com/blog/sendbeacon-is-broken" rel="noopener noreferrer"&gt;above post&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;navigator.sendBeacon()&lt;/code&gt; method &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Asynchronous" rel="noopener noreferrer"&gt;asynchronously&lt;/a&gt; sends a small amount of data over &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/HTTP" rel="noopener noreferrer"&gt;HTTP&lt;/a&gt; to a web server. It’s intended to be used in combination with the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/visibilitychange_event" rel="noopener noreferrer"&gt;&lt;code&gt;visibilitychange&lt;/code&gt;&lt;/a&gt; event (but not with the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/unload_event" rel="noopener noreferrer"&gt;&lt;code&gt;unload&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event" rel="noopener noreferrer"&gt;&lt;code&gt;beforeunload&lt;/code&gt;&lt;/a&gt; events).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Other than blocking the page unloading, the two events &lt;code&gt;unload&lt;/code&gt; and &lt;code&gt;beforeunload&lt;/code&gt; are not reliably fired by the browser as you would expect.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.igvita.com/2015/11/20/dont-lose-user-and-app-state-use-page-visibility/" rel="noopener noreferrer"&gt;Don't lose user and app state, use Page Visibility&lt;/a&gt; summarizes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;beforeunload&lt;/code&gt; is of limited value as it only fires on desktop navigation.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;unload&lt;/code&gt; does not fire on mobile and desktop Safari.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Therefore, on all mobile browsers, if you use &lt;code&gt;sendBeacon&lt;/code&gt; on &lt;code&gt;beforeunlaod&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;beforeunload&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;navigatior&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendBeacon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The callback function which sends the data is never triggered on mobile when the user swipes away or switches app.&lt;/p&gt;

&lt;p&gt;To fix it, you should use &lt;code&gt;visibilitychange&lt;/code&gt; event and &lt;code&gt;beforeunload&lt;/code&gt; together.&lt;/p&gt;

&lt;p&gt;A &lt;em&gt;less wrong&lt;/em&gt; example looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;visibilitychange&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;getState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hidden&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;flushData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hidden&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;beforeunload&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;flushData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;beforeunload&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait? Didn't we just say we should not use &lt;code&gt;beforeunload&lt;/code&gt;? Firing on &lt;code&gt;beforeunload&lt;/code&gt; is still necessary because Safari bug: &lt;a href="https://bugs.webkit.org/show_bug.cgi?id=151234" rel="noopener noreferrer"&gt;visibilitychange:hidden doesn't fire during page navigations&lt;/a&gt; which is still active as Safari Version 14.0.2 (16610.3.7.1.9).&lt;/p&gt;

&lt;p&gt;In practice, you also need to think about what to do with the fact that some clients not firing &lt;code&gt;beforeunload&lt;/code&gt; and some not firing &lt;code&gt;visibilitychange:hidden&lt;/code&gt; and potentially events you fired between last hidden and page unload, etc.&lt;/p&gt;

&lt;p&gt;If you want to play with API and events by yourself and confirm, I've put up a demo at &lt;a href="https://github.com/xg-wang/how-to-beacon/" rel="noopener noreferrer"&gt;https://github.com/xg-wang/how-to-beacon/&lt;/a&gt;. Notice this is not for production, read more below.&lt;/p&gt;

&lt;h2&gt;
  
  
  More on &lt;code&gt;sendBeacon&lt;/code&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Data size limit
&lt;/h3&gt;

&lt;p&gt;The spec (&lt;a href="https://www.w3.org/TR/beacon/#sec-sendBeacon-method" rel="noopener noreferrer"&gt;3.1 sendBeacon Method&lt;/a&gt;) said:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The user agent MUST restrict the maximum data size to ensure that beacon requests are able to complete quickly and in a timely manner.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;em&gt;restrict&lt;/em&gt; is intentionally vague here because the actual implementation is allowed to be different for different browser vendors.&lt;/p&gt;

&lt;p&gt;An important thing to notice is the &lt;em&gt;maximum data size&lt;/em&gt; is for in-flight data that the browser has not scheduled to sent. In other words, if a call to &lt;code&gt;navigator.sendBeacon()&lt;/code&gt; returns &lt;code&gt;false&lt;/code&gt; because exceeding the limit quota, trying to call &lt;code&gt;navigator.sendBeacon()&lt;/code&gt; immediately after will not help.&lt;/p&gt;

&lt;p&gt;When &lt;code&gt;navigator.sendBeacon()&lt;/code&gt; returns &lt;code&gt;false&lt;/code&gt;, a useful pattern is to fallback to &lt;code&gt;fetch&lt;/code&gt; without the &lt;code&gt;keepalive&lt;/code&gt; flag (more on that later), or &lt;code&gt;xhr&lt;/code&gt; without the sync flag. The drawback is you lose the ability to deliver on page unload, but at least during normal sessions the data is not lost.&lt;/p&gt;

&lt;p&gt;If you want to know the actual limit number - it's 64KB (&lt;a href="https://github.com/w3c/beacon/issues/38" rel="noopener noreferrer"&gt;w3c/beacon issue&lt;/a&gt;, &lt;a href="https://github.com/web-platform-tests/wpt/pull/4024" rel="noopener noreferrer"&gt;wpt PR&lt;/a&gt;). However, you should not take that as a guarantee!&lt;/p&gt;

&lt;h3&gt;
  
  
  Delivery is not immediate
&lt;/h3&gt;

&lt;p&gt;Unlike other network API, &lt;code&gt;sendBeacon&lt;/code&gt; can be scheduled and coalesced by the browser. You can certainly contain timestamp data in the beacon payload, but the HTTP request time can be delayed.&lt;/p&gt;

&lt;h3&gt;
  
  
  It may throw error, be sure to catch
&lt;/h3&gt;

&lt;p&gt;If the url parsing has error, &lt;code&gt;sendBeacon&lt;/code&gt; will throw &lt;code&gt;TypeError&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Another case is you can't pass reference without binding &lt;code&gt;navigator&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendBeacon&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nf"&gt;s&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/track&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// ✅&lt;/span&gt;
&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendBeacon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;s&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/track&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;FireFox: &lt;code&gt;Uncaught TypeError: 'sendBeacon' called on an object that does not implement interface Navigator.&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Safari: &lt;code&gt;TypeError: Can only call Navigator.sendBeacon on instances of Navigator&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Chrome: &lt;code&gt;TypeError: Illegal invocation&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Server is encouraged to return 204 No Content
&lt;/h3&gt;

&lt;p&gt;From: &lt;a href="https://www.w3.org/TR/beacon/#sec-sendBeacon-method" rel="noopener noreferrer"&gt;https://www.w3.org/TR/beacon/#sec-sendBeacon-method&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note&lt;br&gt;
Beacon API does not provide a response callback. The server is encouraged to omit returning a response body for such requests (e.g. respond with 204 No Content).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Fetch &lt;code&gt;keepalive&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Beacon API uses Fetch &lt;code&gt;keepalive&lt;/code&gt; under the hood, which is defined in the spec.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/track&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getData&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;keepalive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// Same as 👇&lt;/span&gt;
&lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendBeacon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/track&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;getData&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means they share the same data limitation, remember we discussed when falling back to &lt;code&gt;fetch&lt;/code&gt; you don't need to add &lt;code&gt;keepalive&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;But unfortunately &lt;code&gt;keepalive&lt;/code&gt; has limited browser support, while &lt;code&gt;sendBeacon&lt;/code&gt; is available on all modern browsers.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://caniuse.com/mdn-api_request_keepalive" rel="noopener noreferrer"&gt;https://caniuse.com/mdn-api_request_keepalive&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F8ia54q64krhu60tkyjgj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F8ia54q64krhu60tkyjgj.png" alt="2021-01-19-0857-caniuse-api_request_keepalive"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://caniuse.com/beacon" rel="noopener noreferrer"&gt;https://caniuse.com/beacon&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7yn21x0lvsl6545iko9n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7yn21x0lvsl6545iko9n.png" alt="2021-01-19-0858-caniuse-beacon"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Send Blob data
&lt;/h3&gt;

&lt;p&gt;The second &lt;code&gt;data&lt;/code&gt; param sent with &lt;code&gt;sendBeacon&lt;/code&gt; is &lt;a href="https://fetch.spec.whatwg.org/#bodyinit" rel="noopener noreferrer"&gt;&lt;code&gt;BodyInit&lt;/code&gt;&lt;/a&gt;, which means you can use &lt;code&gt;Blob&lt;/code&gt; to create the data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;world&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Blob&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendBeacon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/track&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When creating a &lt;code&gt;application/json&lt;/code&gt; type request, it is no longer a simple request, and will trigger CORS preflight request. See &lt;a href="https://xgwang.me/posts/a-practical-guide-to-cors/" rel="noopener noreferrer"&gt;A practical guide to CORS&lt;/a&gt; if you're not familiar with CORS.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cannot use with compression API
&lt;/h3&gt;

&lt;p&gt;There's a new API you can use to compress data on client side: &lt;a href="https://github.com/wicg/compression/blob/master/explainer.md#deflate-compress-an-arraybuffer" rel="noopener noreferrer"&gt;compression&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But it won't work with &lt;code&gt;sendBeacon&lt;/code&gt; or Fetch &lt;code&gt;keepalive&lt;/code&gt;, fetch will throw error when the &lt;code&gt;keepalive&lt;/code&gt; request has stream body.&lt;/p&gt;

&lt;h2&gt;
  
  
  Service Worker
&lt;/h2&gt;

&lt;p&gt;The service worker can operate async after the original document closes. (&lt;a href="https://twitter.com/wanderview/status/1234126801049444352" rel="noopener noreferrer"&gt;Tweeter thread&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Ideally, you can put all the existing data processing logic and beaconing to a service worker, to execute code &lt;a href="https://web.dev/off-main-thread/" rel="noopener noreferrer"&gt;off the main thread&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  End word
&lt;/h2&gt;

&lt;p&gt;Beacon is a simple API, but there are complexities coming from the heart of UI engineering. Use it with caution and always check your data.&lt;/p&gt;

</description>
      <category>web</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
