Timing a specific fetch call
Asked Answered
B

5

13

I am making a fetch call like so fetch("foo.com/x.json") and would like to get the time it takes to make the request as it is reported in dev tools.

I have already tried,

performance.mark("fetch-start");
let start = performance.now();
let res = fetch("https://example.com/foo.json", {});
res.then(r => {
  performance.mark("fetch-end");
  performance.measure("fetch", "fetch-start", "fetch-end");
  var measures = performance.getEntriesByName("fetch");
  var measure = measures[0];
  console.log(measure);
});

Have also tried performance.now() -start and they are not as accurate as the devtools, I'm guessing this is due to the fact that the browser does more than one thing at once and doesn't spend all it's time measuring things in isolation.

Is there a way to get as accurate as Developer tools for network timing?

Byebye answered 21/8, 2018 at 16:34 Comment(2)
@JamieWeston it isn't the same, I've even listed a similar solution as "what I've tried" in the question body.Byebye
Did my answer help?Chairman
C
7

Answer:

Try wrapping the code below in an async function and using performance.now()


Browser JavaScript

async function checkResponseTime(testURL) {
    let time1 = performance.now();
    await fetch(testURL);
    let time2 = performance.now();
    return time2 - time1;
}

(async () => {
    // for the purpose of this snippet example, some host with CORS response
    console.log(await checkResponseTime('https://geoapi.pt'))
})();

Node.JS

let fetch = require('node-fetch');
async function checkResponseTime(testURL) {
    let time1 = performance.now();
    await fetch(testURL);
    let time2 = performance.now();
    return time2 - time1;
}
console.log(await checkResponseTime('https://stackoverflow.com'))
Chairman answered 12/3, 2021 at 21:10 Comment(3)
I am using vanilla JavaScript and Angularjs. How can I use this at them?Lashondra
This will work with vanilla JS. For using with angular: declare an asynchronous function, store performance.now() in a variable, fetch the url, and return the current performance.now() subtracted by the stored variable.Chairman
@YoshidaAtsushi, if my suggestion didn't help, ask a new question. I am not so familiar with angular.Chairman
P
7

There is a Resource Timing API being defined that will help us do just this.
Note that this is still a draft and prone to change in the future, but it's already available in the three main browser engines.

const url = "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png?" + Math.random();
// create PerformanceObserver
const resourceObserver = new PerformanceObserver( (list) => {
  list.getEntries()
    // get only the one we're interested in
    .filter( ({ name }) => name === url )
    .forEach( (resource) => {
      console.log( resource );
    } );
  // Disconnect after processing the events.
  resourceObserver.disconnect();
} );
// make it a resource observer
resourceObserver.observe( { type: "resource" } );
// start the new request
fetch( url, { mode: "no-cors" } );
Propertied answered 30/3, 2021 at 5:40 Comment(0)
I
3

You will never get consistently accurate results in JS land with regards to Dev Tools.

You will sometimes get consistently accurate results in JS land with regards to what a user perceives.

What I mean by this is two fold:

  • DevTools measures network time for fetching a resource
  • JS land measures network time + loading + handoff back to JS when the resource is fully available

A simple experiment can be done:

performance.mark("fetch-start");
const start = performance.now();
const res = fetch("https://example.com/foo.json", {});
res.then(measure).catch(measure);

someHeavyOperation();

function measure(r) {
  performance.mark("fetch-end");
  performance.measure("fetch", "fetch-start", "fetch-end");
  var measures = performance.getEntriesByName("fetch");
  var measure = measures[0];
  console.log(measure);
}

function someHeavyOperation() {
    /* block for 60 seconds! */
    const target = Date.now() + 60*1000;
    for (;;) {
        if (Date.now() > target) {
            break;
        }
    }
}

You can replace someHeavyOperation with many things happening on your computer, including but not limited to:

  • other JS code on the page
  • rendering that is being updated
  • this measurement being in an idle background tab
  • some other process, unrelated to the browser, that's blocking the CPU
  • the browser, loading the result to memory and making it available to JS
  • any combination or all of these together in perfect harmony blocking your measurements

P.S.: You can test the JS handoff time by trying to load a very large (and nested) JSON file. Similar delays should be noticed with JSON.parse on a large string.

Incurrence answered 14/3, 2021 at 3:11 Comment(4)
what do you recommend?Frigging
Depends on your use case, but generally having an isolated environment helps (in terms of not artificially skewing your results). Also doing multiple runs, identifying a standard deviation and a confidence interval can give you better, but not perfect results... @Frigging what's your use case ?Incurrence
Thanks for the reply! We want to add network timings to our Postman alternative, hotpot.ai/stubbi. Any suggestions on the best way to do this?Frigging
Thanks for the reply, @Khez. Someone deleted the previous comment. The use case is a way to measure network requests made from a web page (think web-based REST tool). Any suggestions?Frigging
K
1

Your question assumes that what you see in DevTools is accurate. It might be a well-founded fact, or an assumption that was not yet proven. I would not try to get close to the values seen in Dev Tools, because however close I may get to Dev Tools' measurements, my results are at best as accurate as Dev Tools' measurements.

So, I would host locally a proxy, that would catch all the requests from the browser to the given site and pass to the server and then get the server's response and send it to the browser. The sole reason for the use of this proxy is to log the time between passing a request received from the browser to the server and the time the response arrives. That would be a very accurate way to monitor times and it would make you intependent of the possible problems of DevTools. In general I do not bother to do this, as the measurements I make are good-enough, but if you want top-notch precision, then I recommend this approach.

Kuehl answered 14/3, 2021 at 2:17 Comment(0)
A
1

Maybe calling DevTools API via CPD using Node.js and retrieving DevTools timing could be acceptable ?

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch()
  const page = await browser.newPage()
  await page.goto('https://page-to-test')

  const resourceTimingJson = await page.evaluate(() =>
    JSON.stringify(window.performance.getEntriesByType('resource')))

  const resourceTiming = JSON.parse(resourceTimingJson)
  const fetchResourceTiming = resourceTiming.find(element => element.initiatorType.includes('fetch'))

  console.log(fetchResourceTiming)

  await browser.close()
})()

JScript inspired from this link

EDIT: Adding a script focused on the duration :

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch()
  const page = await browser.newPage()
  await page.goto('https://page-to-test')

  const resourceTimingJson = await page.evaluate(() =>
    JSON.stringify(window.performance.getEntriesByType('resource')))

  const resourceTiming = JSON.parse(resourceTimingJson)
  const fetchResourceTimingAll = resourceTiming.filter(element => element.initiatorType.includes('fetch'));
  if (fetchResourceTimingAll) { fetchResourceTimingAll.forEach( element=>console.log(element.name+" : "+element.duration)) }

  await browser.close()
})()
Antipyrine answered 17/3, 2021 at 17:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.