How to make javascript fetch synchronous?
Asked Answered
S

9

93

I'm using fetch to get data json from an api. Works fine but I have to use it repeatedly for various calls, thus it needs to be synchronous or else I need some way to update the interface when the fetch completes for each component.

function fetchOHLC(yUrl){
    fetch(yUrl)
    .then(response => response.json())
    .then(function(response) {
                alert(JSON.stringify(response.query));

            var t = response.created;
            var o = response.open;
            var h = response.high;
            var l = response.low;
            var c = response.close;
        
        return {t,o,h,l,c};
    
    })
    .catch(function(error) {
        console.log(error);
    });    
}

var fetchData = fetchOHLC(yUrl);
alert(fetchData); // empty ?

Is there any other way to achieve it other than using fetch? (I don't want to use jquery preferrably).

Thanks

Edit

The question is about fetch-api, not ajax, not jquery, so please stop marking it as duplicate of those questions without reading it properly.

Sop answered 24/6, 2017 at 10:59 Comment(1)
Voted to reopen, as synchronous requests do have valid use cases. (E.g. chrome.webRequest.onBeforeRequest.addListener won’t let you do asynchronous operations, leaving you no choice.)Backlog
F
27

fetch is intended to do asynchronous calls only, but there are some options:

Option 1

If XMLHttpRequest is also fine, then you can use async: false, which will do a synchronous call.

But there is a deprecation warning in the whatwg specs since 2014 and some browsers are already complaining in the development tools. It can therefore be assumed that sooner or later it will no longer work.

Option 2

Use async/await which is asynchronous under the hood, but feels like it is synchronous, see https://mcmap.net/q/225850/-how-to-use-fetch-with-async-await-closed

Option 3

or else I need some way to update the interface when the fetch completes for each component

This sound like fetch + Promise.all() would be a good fit, see https://mcmap.net/q/225851/-using-fetch-api-with-promise-all

Option 4

If you want to send analysis data or session data when leaving a page (e.g. in onbeforeunload) and want to be sure that the data is sent to the server, which is not guaranteed with a normal asynchronous AJAX call, you can use the Beacon API with Navigator.sendBeacon().

Fishwife answered 9/6, 2022 at 14:1 Comment(4)
I can't believe that there's no way to do it. There's a legit case in which it only makes sense to make a sync xhr. In the beforeunload event handler. And this means that in the project I am workin on right now, I'll have to mock axios because they used it. And use XMLHttpRequest instead. Funny isn't it?Neurath
@IharobAlAsimi I can only guess for what purpose you want to make an AJAX call in the onbeforeunload handler, but I've added option 4, so maybe that fits your problem.Fishwife
I start a payment flow, and I want to ensure it is cancelled even if the user closes the tab. Not an easy thing unfortunately.Neurath
That should probably not rely on something user side, backend timeout or keepalive might better fit your case hereJutland
C
6

Hi this is 2023 and I had to make a syncronous JS fetch for my particular scenario. This is the code that worked for me:

const request = new XMLHttpRequest();
request.open("GET", "/bar/foo.txt", false); // `false` makes the request synchronous
request.send(null);

if (request.status === 200) {
  console.log(request.responseText);
  return request.responseText;
}
return null;
Clarita answered 13/10, 2023 at 17:39 Comment(0)
K
2

You can use await, but await only can be used in async function, so you need to wrap your top-level code in async function

function fetchJSON(url) {
    return fetch(url)
        .then(response => response.json())
        .catch((error) => {
            console.log(error);
        });
}

(async () => {
    var json = await fetchJSON('https://api.github.com/users/steelywing/repos');
    alert(json);
})();

But if you can use .then(), better use fetchJSON().then()

fetchJSON('https://api.github.com/users/steelywing/repos')
.then((data) => {
    alert(data);
});
Kinesics answered 7/2, 2024 at 12:7 Comment(1)
FWIW I just tried await fetch(url) bare in my browser console (Firefox 124.0.2) and it worked! Which is great. Thanks!Dirndl
R
2

I had the exact same problem - I was calling fetch from a method on beforeunload and it sometimes triggered, sometimes didn't.

I found this article: https://web.dev/articles/disallow-synchronous-xhr

So I removed the async/await from the event handler and added keepalive: true to the fetch options. Seems to be working consistently.

Before:

save: async function () {
    const requestOptions = {
        method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify(data),
    };
    const response = await fetch(url, requestOptions);
}

window.addEventListener("beforeunload", async function (event) {
    await self.save();
});

After:

save: async function () {
    const requestOptions = {
        method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify(data),
            keepalive: true,
    };
    const response = await fetch(url, requestOptions);
}

window.addEventListener("beforeunload", function (event) {
    self.save();
});
Rhoades answered 15/3, 2024 at 16:57 Comment(0)
A
-1

If you don't want to use the fetch api you will have to use callbacks, event listeners, XMLHttpRequest.

Acescent answered 8/6, 2022 at 21:23 Comment(1)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Baton
O
-2

You always can use the old fashion way using xhr. Here a example of a request.

   var xhttp = new XMLHttpRequest();
   xhttp.onreadystatechange = function() {
      if (this.readyState == 4 && this.status == 200) {
        document.getElementById("demo").innerHTML = this.responseText;
       }
     };

   xhttp.open("POST", "cookies.php", true);
   xhttp.send();
Oyler answered 1/2, 2023 at 15:11 Comment(1)
Did you mean to set the 3rd parameter to to false for async?Dipetalous
D
-4

You simply need to add callback function as parameter.

function fetchOHLC(yUrl, callback){
    fetch(yUrl)
    .then(response => response.json())
    .then(function(response) {
            alert(JSON.stringify(response.query));

            var t = response.created;
            var o = response.open;
            var h = response.high;
            var l = response.low;
            var c = response.close;
        
            callback({t,o,h,l,c});
    
    })
    .catch(function(error) {
        console.log(error);
        callback(null, error);
    });    
}

fetchOHLC(yUrl, function(response, error){
    if (response == null) {
        console.log(error);
    } else {
        var fetchData = response;
        alert(fetchData);
    }
});
Disease answered 27/9, 2022 at 2:51 Comment(0)
T
-4

This is how I have implemented and it's working perfectly fine. The real hero here is the JS fetch API.

var url = "http://excample.com/get/bytes"
var audio;
audio = $('audio');

fetch(url, {
    method: 'GET',
    headers: {
        'Authorization': 'Bearer ABCDEFGHIJKLMNO'
    }
}).then(response => {
    response.blob()
        .then(blob => {
            const objectUrl = URL.createObjectURL(blob);
            audio[0].src = objectUrl;
        }).catch(e => {
            console.error(e);
        })
}).catch(error => {
    console.error(error);
});
Tuberous answered 3/5, 2023 at 16:0 Comment(0)
J
-16

If you came here because you dropped "how to make javascript fetch synchronous" into a search engine:

That doesn't make much sense. Performing network operations is not something which requires CPU work, thus blocking it during a fetch(...) makes little sense. Instead, properly work with asynchrony as shown in the duplicate linked above.


In case you really need a synchronous request (you don't), use the deprecated XMLHttpRequest synchronous variant, to quote MDN:

Note: Starting with Gecko 30.0 (Firefox 30.0 / Thunderbird 30.0 / SeaMonkey 2.27), Blink 39.0, and Edge 13, synchronous requests on the main thread have been deprecated due to their negative impact on the user experience.

const request = new XMLHttpRequest();
request.open('GET', '/bar/foo.txt', false);  // `false` makes the request synchronous
request.send(null);

You can find more information on MDN.

Jabberwocky answered 24/6, 2017 at 11:4 Comment(17)
why are we using return twice in it?Sop
oh ok, sorry i missed that the return in closure returns to fetch only not fetchOHLCSop
@NabeelKhan no it returns into the promise chain. So it resolves the promise returned by then with that. thats why ive returned that promise, so you can get the fullfilled data...Jabberwocky
This provides sound advice that the question might be misguided, but doesn't answer the question.Pinhead
I am not an expert, but what about (initial) values that are absolutely needed from the api without which the whole rest of the javascript does not make any sense?Thar
@johannes have a look at "top level await". Also much better to show some progress bar / loading spinner than to just block the browser somehow.Jabberwocky
Top level await only works in modules. Sometimes you need to load in your configuration settings before you go into modules (or you are not using modules).Horrorstruck
I know I am commenting on a four years old question, but in some rare cases, making a synchronous web request makes sense and is legitimate in my opinion. For example, I am making a synchronous web request to download OAuth settings required by my API (client_id and authority) stored in a static file (e.g. settings.json). The file can also be heavily cached using cache-control. MDN Web Docs explain how to do that: developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/…Stu
Yeah this is a terrible answer, it's a shame that the stack overflow community is full of so many people who think like you. The question is "how to make a synchronous fetch request in javascript" which your answer does not answer, instead you chose to answer condescendingly about how a normal fetch could be done.Halsted
@matthew well if you really want that answer: Use a synchronous XmlHttpRequest. It is deprecated in all browsers though. You're welcome.Jabberwocky
@JonasWilms XMLHttpRequest may be deprecated, but still there are some strictly synchronous Browser APIs in your Browser. Today. Like WebRequest, the only valid way to secure a Browser (intercepting SSL is invalid). And if you do not want to re-invent the wheel using JavaScript every time (aka bloat the Browser) you need strictly fully synchronous web requests at that level. Async, await, Promises or other Callbacks cannot be used here. In all current Browsers. (Chrome tries to stop to support WebRequest with Manifest v3. Looks like Mozilla is a bit more clever in that respect ..)Recitation
@Thar Instead of init(); main(); call init().then(() => main()) and use async function init() { ..; await fetch(URL); .. }. However this only works in top level. But not in callbacks of webRequest/onBeforeRequest which must return something strictly synchronously, so you cannot wait for something asynchronous in such callbacks.Recitation
I have issues with submit in an Angular PWA and its event not being handled using async await Promise. I am using old school XmlHttpRequest and it works. Sync could be a working option in some cases. Maybe I am erroring in some other way though.. just wanted to give an example of async not working in some cases.Allround
I do know fetch does not need to be synchronous, but Chrome extensions’ chrome.webRequest.onBeforeRequest.addListener won’t let me do asynchronous operations. We’re here because we have our own good reasons.Backlog
Well, thats a temporary limitation "From Firefox 52 onwards, instead of returning BlockingResponse, the listener can return a Promise which is resolved with a BlockingResponse" ...Jabberwocky
IMO, another reasonable example is where the end user is me. I'd rather wait a couple TCs for my page to render than write more code to enhance my own experience.Cailean
@RobertoPrevato I need to do exactly what you are doing. It seems silly to have to string up promises etc. when just having an sync method would solve the problem very simply.Phrenology

© 2022 - 2025 — McMap. All rights reserved.