Web App Performance

Performance Observers

Maximiliano Firtman

Maximiliano Firtman

Independent Consultant
Web App Performance

Check out a free preview of the full Web App Performance course

The "Performance Observers" Lesson is part of the full, Web App Performance course featured in this preview video. Here's what you'd learn in this lesson:

Max shares the supported entry types in the PerformanceObserver API, allowing developers to access metrics like the core web vitals directly from JavaScript.


Transcript from the "Performance Observers" Lesson

>> There is a whole working group at the W3C, it's a performance working group, and they're creating a lot of other specs that some browsers are implementing with time. Typically, Chromium browsers are the one implementing these API first, and Safari and Firefox are a little behind, okay? But there are a lot of APIs.

So every API actually worked with that performance property that we saw before. So everything is there. And we have on modern APIs, entries. And that entries are collections of metrics. And we can query entries using filters. I know it sounds weird, but let me show you some samples.

For example, you can get navigation entries. Those are the entry we saw before. We can get resource entries, the spec is known as a Resource Timing API. In this case, you can get the DNS resolution, the TCP timings, the download times for every resource, for every PNG, for every CSS, for every JavaScript, okay?

You can say a mark, that's known as User Timing. In this case, you can mark your own events in the timeline. So in this case, you say, okay, my tweet is ready, if you're Twitter. My tweet is ready, or something is rendered. And then you can actually get an observation of when that happen, okay?

So you can create your own custom metrics with User Timing API. And if you use User Timing API, you can measure, so you can calculate. I have this measure and that one, well, give me the duration of that process and I do something with that. Analytics, making a decision on the fly, then you wanna change something.

So the old version to getEntries is performance .getEntriesByType. That's deprecated. It's still working, but it's deprecated. The properties are name, entryType, startTime, and duration. And I know it's still feels a little weird, we will show that in code in a minute. And the new version, instead of getting the entries, we create observers.

Why? Because sometimes we don't have the data yet, we need to wait for it. So now we create observer. They are listeners. So we observe for those kind of performance moments. For example, let's see what you see here in action. PerformanceObserver.supportedEntries. If we go here to Chrome, and we talk to the performanceObserver, and we asked for the entry types.

So Chrome supports all these types of observers. So for example, I can observe largest-contentful-paint. Which is good, so now you know what that is. It's the most important metric that we saw from Web Vitals. So which means that if I create an observer over that entry, then when the browser has said, we had the winner, we know which element is the largest-contentful-paint, and we have the metric.

It will execute our observer, and we will receive the data. We will know the LCP in JavaScript. Does it make sense? First-input delay is this one. This is CLS. So actually from Chrome, you can actually get the three Web Vitals in JavaScript just by creating an observer over the performance options.

Let's say, for example, longtask is the observer you will use to know when a task is over the budget. What's a task? A JavaScript thread. If you have some JavaScript code that is looping through 1,000 elements and doing something with that, it's taking too much time. Well, the observer will receive the information.

That script, that line, it has just execute code for 100 milliseconds. It's over the line. And then you can query your server. But not every browser supports the same kind of types. That's not what I want. If I open Safari, we inspect here. We go to the console.

We only have five kinds. We have mark, measure, navigation, paint, and resource. So we don't have Web Vitals. We have paint, that's first paint. First paint is when something appears on the screen, that doesn't mean it's content or it's useful, okay? So how does this work? We can actually try this directly in our code.

You can see it's actually pretty simple, it's not so complicated. For example, in our script, we create a new PerformanceObserver, okay? Just that. And we observe different entries. For example, here I can say which entries do I want? For example, I want largest-contentful-paint. We are talking about this.

I want largest-contentful-paint. And it's an array. So if I want all the web vitals, I can also pass the other two. But for some reason, they don't have the full name. So it's not CLS, content layout shift. It's just layout shift. Who knows? And the other one is also first-input and not.

The second argument is buffered, true or false. Why is that? If you say true, when you start observing events, maybe the event has already happened. Does it make sense? So if you say buffer true, you're asking, hey, if the event already happened, give me a call anyway, immediately.

I wanna receive that. Buffered false will just give me an execution only from now on, not if the data is already buffered. Does that make sense?
>> So is it false in Safari?
>> In this case, I mean, Safari also supports true and false, but Safari will not support these entries.

So it will never execute my. I can also add paint, that's the one supported by Safari, for example. And there, it's going execute your observer and you will receive entries. And for now, I can just do console.log just to see what's going on. Does it make sense? I mean, the code is not really complicated, the thing is understanding that that's a possibility, that that's available.

So I'm going to refresh, and now I'm seeing some outputs. I have actually two outputs. One, it seems like at the same time, we've got first-paint and first-contentful-paint. Why we've got it at the same time, probably because they were buffered. I don't know, but maybe. And each entry has the startTime.

They happen at the same time, first-paint and first-contentful-paint. And then we have the largest-contentful-paint that if you try again, it takes some more time. This is the loadTime, the renderTime, okay? And even if we receive the URL of the image that is actually pointing to the largest-contentful-paint.

And by the way, we can go back to our delay thing just to see this in action. And we can even increase the maximum delay, so it's not so immediate. So now we get this. In this case, you can see it started at 2.7 seconds, largest-contentful-paint. So that's the LCP.

Okay, does that makes sense, how that works? Why do you want the data? It's up to you. Maybe you want to send the data to the server. Maybe you wanna make a decision on the fly. Like, hey, I mean, loading this app took lot of time. So from now on, let's load a basic version of the app.

Or let's jump into a standard version instead of high definition, whatever. So the API is just giving you the data. It's up to you what you're going to do with that. If I try this on Safari, because it's not supporting the whole API, we should get only the entries that are important for Safari, and that's first-paint.

First-contentful-paint also known as first-paint, it happened in almost five seconds. We don't have the largest-contentful-paint here, we have the first-contentful-paint.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now