This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 7 years ago.
What is the best way to just grab the content of a .json and store it to a var ?
I've seen some threads but i just can't manage on my code...
function retrieve_json() {
var content;
$.when(
$.getJSON("http://localhost/browsergame/data/test.json", function(data) {
content = data;
})
).then(function () {
console.log(content);
return content;
});
}
var json_content = retrieve_json();
console.log (json_content);
the console.log within the function displays the correct content of the json, the console.log at the very end of my code shows 'undefined'. I guess the async is the main issue here. I have tried doing it with $.ajax but couldn't manage either.
So, what is the best way to just grab the content of a .json file and store it to a var ?
The problem is that retrieve_json wont stop and wait until the json arrives from the server. The execution continues with whatever is after the statement starting with your $.when. In this case it's the end of the function. Since there is no return statement here, the function returns with the value 'undefined', what you store in json_content.
If you want to stop execution until the answer returns you have to wrap the next steps into a function and for example call it in the 'then' method of your example or just pass it to the getJSON where you store it in the content variable now.
And make sure you read up on how promises work.
Once code is async, it can't be made synchronous. You can only reliably use the content of your JSON in functions that you pass to promises or as callbacks, otherwise you have no guarantee that your JSON has been fetched when your code gets executed.
If you find even the promises syntax horrible you might be interested in async/await, a proposed syntax for ES7 that can be transpiled to ES5 right now with Babel.
Here's a good article which demonstrates the different approaches to asynchronous programming in JavaScript: The long road to Async/Await in JavaScript.
Related
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 2 years ago.
Ok, so I'm trying to modify a piece of existing code in a function I'm working on to integrate a database call to check for information stored there instead of just locally as is currently default in the library (commando.js).
The problem is, the database call is handled through a promise with sequelize. I need to return values from the .then() somehow to the parent function/scope? I know you can do this with async/await but because the function is part of a class that extends one from the library, it can't be an async function. Is there a way to return the value from .then() to the parent scope or is that simply not possible?
I've tried to use callbacks and they get the value back out to the parent scope but the function finishes executing before the callback returns so I don't think that'll work unless there's a way to wait for the callback to return?
Here's what I currently have:
nonAsyncFunction(args){
// Do stuff
database.getData()
.then((result)=>{
// Do Stuff
return value
});
return value // Need to somehow return value from .then() here?
}
If you have any questions or need any info from me feel free to let me know, otherwise thanks so much in advance for the help.
By design, this is not possible. You cannot synchronously wait for the resolution of a Promise, so you can't return the value a Promise resolves to.
This is because, for example, <script> elements in HTML without the defer attribute block parsing and rendering. It would be really bad if someone made made a synchronous HTTP request in them. That's why synchronous XHR on the main thread was deprecated.
I'm quite new to using Javascript and particularly JSON, and I've been struggling to do this:
There is a JSON file on my web server which I am trying to access and parse into a JavaScript object. What I am trying to do is parse that JSON into an array and then further manipulate that array based on other user variables.
This is a sample of what the JSON looks like:
{"log":
[{
"name":"Al",
"entries":[8,12,16,19]},
{"name":"Steve",
"entries":[11,17,22]}]}
What I need to do is retrieve the array for one of the entries and store it in an array as a JavaScript object. What I have tried to do is this:
var entriesLogged;
fetch ('url to the json file').then(function(response){
return response.json();
}).then(function(data){
entriesLogged = data.log[0].entries;
});
However, I can't seem to get this to work and to assign the value to the variable in a way that persists outside of this scope. I have been able to output the value of the array using console.log, but I have not been able to actually work with and manipulate that data like an object. I'd ideally like to parse the JSON file from the server onto a global array.
Most of the tutorials I've come across so far have used the JSON file to output console logs or change the contents of html elements, however I need to retrieve the values from the JSON into a global array first.
Am I missing something here? Does anyone have advice on how to do this?
Best wishes,
Dom
Are you trying to manipulate the entriesLogged variable after the fetch promise chain? The fetch is asynchronous so this means that any code after the fetch chain will run before the fetch chain finishes.
var entriesLogged;
fetch('url to the json file').then(function(response){
return response.json();
}).then(function(data){
entriesLogged = data.log[0].entries;
});
// Code down here will run before the fetch chain finishes
// So if you're using entriesLogged down here, it will be null
console.log(entriesLogged); // entriesLogged is null
You might want to do something like:
.then(function(data){
entriesLogged = data.log[0].entries;
myFunction(entriesLogged);
});
function myFunction(myData) {
console.log(myData); // can access entriesLogged here
}
What is happening is that the call is async, it means that when you make that call a new thread is created and javascript automatically passes to the next line.
var entriesLogged;
fecth(something).then(function(data){entriesLogged = data[0]}); // this goes to another thread an takes e.g. 5 miliseconds but javascript does not wait for this to complete, it goes to the next line
console.log(entriesLogged); // this is executed before the call is finished
There are TONS of "answers" for variations of this question that don't actually solve the problem, they just describe the issue. (tl;dr solution is last block of code)
The issue is that .fetch('URL.json') and .json() are "asynchronous" calls. So the script will start working on that call, then continue on to the rest of the commands in your script while the .fetch() and .json() calls are still being worked on. If you then hit a command BEFORE one of these asynchronous calls is done, the rest of your script will have no data to work with.
There are tons of places to look for fully understanding the ins and outs of how async calls work in JS, for example here:
How do I return the response from an asynchronous call?
If you're like me and you just want the code to function synchronously, the quick and dirty solution is to create an async main loop and then to make sure you use the await keyword when calling asynchronous functions that you need to WAIT for the data to be populated on.
Example main loop:
(async () => {
// put main loop code here
})();
You would then put your code inside that main loop BUT you have to make sure that you're adding the await keyword in front of every asynchronous call you use. in the original example provided, you'll have to declare your .then() functions that need to use await as async as well. This is confusing if you don't fully understand the asynchronous nature of javascript and I'll simplify it later, but I wanted to provide a working version of the original code provided so you can understand what changed:
(async () => {
var entriesLogged;
await fetch('url_to_the_file.json')
.then( async function(response){
return await response.json();
})
.then( function(data){
entriesLogged = data;
});
})();
The .then() call is typically used for post-processing so you can do work inside of the asynchronous call... but since in this example we're deliberately making it synchronous, we can avoid all of that and clean up the code by doing individual simple commands:
// Start main loop
(async () => {
let response = await fetch('url_to_the_file.json');
let entriesLogged = await response.json();
})();
That should get you what you're looking for and hopefully save anyone like us the heartache of spending hours of time trying track down something that there is a simple solution for.
Also want to give a huge call out to the place I got the solution for this:
https://www.sitepoint.com/delay-sleep-pause-wait/
I have hundreds of master JS scripts which look like this and which I may not modify:
> some code filling parameter1
var response1=callAjax(parameter1);
> some code using response1 and filling parameter2
var response2=callAjax(parameter2);
> etc ..
The callAjax function can be changed or re-written. It currently calls Ajax in synchronous mode - which is deprecated - so it needs to be overhauled. Blocking the UI during the execution of the master file is a requirement (these are banking and similar applications so the end user is not allowed to edit any input fields or click any button during the process - a screen veil can be shown).
How can I modify the callAjax function to use async mode and leave the master scripts (hundreds or thousands of them) un-changed ?
I looked at using Promises or async/await but cannot figure how to use these in order to return the ajax response to the master scripts. Same with adding some global variables used as semaphores. Again, breaking the master scripts into several functions for each call is not allowed. These are plain Javascript, no jQuery.
How can I modify the callAjax function to use async mode and leave the master scripts (hundreds or thousands of them) un-changed ?
You can't. If you use asynchronous ajax, then you can't return the value from the function because the function will return BEFORE the value is available. You simply can't make an async operation into a synchronous function. For a lot more detail on returning asynchronous operations from a function, see How do I return the response from an asynchronous call?. You will see that all the available options involve calling a callback when the async value is done or returning a promise and using a callback with .then() on the promise to know when the value is done and what it is.
If your requirement is that you block the UI and that you can't change the calling code, then you have to stick with synchronous Javascript. No asynchronous operation will do either.
Otherwise, get rid of those requirements and write proper asynchronous calling code and don't block the UI in this way (you can protect the contents of the screen from editing or button presses during the ajax operation with different techniques).
So, my conclusion from the problem you've described is that you need to lift some of your current requirements because there is no solution that:
Blocks the UI
Uses only asynchronous Ajax
Returns the ajax obtained value directly from callAjax()
Does not change the calling code that calls callAjax()
You can't do all of those at once.
The forward moving design choice would be to return a promise from callAjax() and fix all the calling code that uses it to use that promise. For blocking the UI, you'd have to use some sort of UI protection (screen veil, you called it, or something like that).
My advice if you have a large body of code using this old synchronous ajax function is to create a new ajax function with a different name that returns a promise and deprecate use of the old one for any new code (make all new code use the new one). Then, as needed and guided by business goals, rewrite uses of the old function to use the new one one at a time. This will at least stop more code being written with the old, synchronous ajax code. And, it will give you an opportunity over time to convert code to the new interface as needed.
Note that the closest thing you could get to calling code that looks close to what you have (but still requires changes) would be using async/await (available in the ES7 specification or in transpilers). You'd return a promise from callAjax() and then, the caller can await the result:
// async function returns a promise
async function someFunction() {
try {
// some code filling parameter1
var response1 = await callAjax2(parameter1);
// some code using response1 and filling parameter2
var response2 = await callAjax2(parameter2);
// etc...
} catch(e) {
// handle error here
}
}
P.S. It looks like your current code doesn't have a good way to communicate back ajax errors.
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 7 years ago.
In my honest opinion this question is not duplicated because I know how to fix it using a callback as explain in the linked question, this question is specific about a situation where I cannot modify the outfer function and this is not covered in the another question
I use a library that I cannot modify, the library do something like this:
var result = Foo()
Then it uses result for the work. I can not modify how result works, I can only modify Foo because I pass it as parameter to the library.
If foo has something like this
funcion Foo(){
return "Hello"
}
It works perfect. Result is Hello and it works.
The issue is that my Foo has an async call inside it. It calls a web server and then it should return the response of the server. So
funcion Foo(){
request.get('http://server.com', function(error, response, body){
var parsed = JSON.parse(body);
return parsed.Hello;
});
}
In this case when var result = Foo();, result does not contains the response of the server (because is async) and continue with an empty result.
So if I only can modify Foo what should I do?
Update: If you dont find it possible I would love to hear alternatives like what should I ask in PR for the library, or a way to force the request to be syncronous
This is how you can work with a synchronous request:
funcion Foo(){
var request = new XMLHttpRequest();
request.open("GET", "http://server.com", false);
request.send();
function handleResponse(response) {
console.log(response);
}
handleResponse(request.responseText);
}
If you can only modify Foo, then modify it to send the request synchronously.
What you're trying to do is impossible per se, once code is asynchronous, it can't be used as you would synchronous code. That's why callback hell is a thing.
You could try to analyze the module internals and monkey patch it, but that may not be possible depending on what exports the module exposes.
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 7 years ago.
Like a person asked here (but his solutions as to call a nother function) https://stackoverflow.com/a/10796326/315200 ...I would like to know if its possible to have a function which doesn't call a second function on response of an async request, but simply return when the async request responses.
Something like this maybe:
function callToFacebook() {
var fbResponse;
FB.api('/me', function (response) {
fbResponse = response;
});
return fbResponse; //Will return undefined because CallToFacebook is async
}
Isn't that possible some way, without calling another function??
What I'm trying to achieve is to have one function I can call with some parameters, which will return the response object from a async webservice, like FB.
In short, no. You cannot have an asynchronous function return a meaningful value synchronously, because that value does not exist at that time (as it is built asynchronously in the background).
You can, however, return a Promise object, representing the "potential return value" of the asynchronous operation, and bind a function to that object using done() or similar. That way, your function gets return semantics (instead of having to chain the control flow into a callback), and remains asynchronous.
No, it's not possible.
You can't return value that is returned from async operation.
Think about it, you tell 10 people to have one mile running contest, they start now, will finish in one minute +-, but you want to know the answer now, it's not possible unless you're cheating...