JS - Assigning a variable while using a callback [duplicate] - javascript

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 5 years ago.
I'm relatively new to JS, and have been stuck on this problem for a while. I am trying to assign a variable from the return value of a function, like this:
var data = makeApiRequest(url);
However, the code continues before the makeApiRequest() function completes, causing data to be undefined.
I've been researching the problem for a while and found that the problem can be fixed with a callback. My issue is I'm not sure how to get data assigned to the result of makeApiRequest() in doing so.
I found this example (on callbackhell.com) that I think perfectly illustrates my problem:
var photo = downloadPhoto('http://coolcats.com/cat.gif')
// photo is 'undefined'!
Author's solution:
downloadPhoto('http://coolcats.com/cat.gif', handlePhoto)
function handlePhoto (error, photo) {
if (error) console.error('Download error!', error)
else console.log('Download finished', photo)
}
console.log('Download started')
The thing that I cannot figure out for the life of me is how photo is assigned (or even declared) in this example. I feel like if I could crack that, I could figure out my specific problem. Any help would be greatly appreciated. Thanks very much in advance.

makeApiRequest(url) is an asynchronous call. That means that if you try to evaluate it immediately after calling it, it will be undefined. The modern solution is to use a Promise.
However, a callback function is perfectly acceptable if your logic won't go layers deep. You can do so by altering the parameters on your makeApiRequest function like so:
function makeApiRequest (url, callback) {
// make the api call, and then:
callback(results);
}
Now, you will call makeApiRequest() as such:
makeApiRequest (url, function(data) {
// handle your data here.
console.log(data);
});

When download function completes, it invokes callback function handlePhoto with data (photo) passed to it as a second argument, so that it can be used inside of the handlePhoto function.

If you want to assign the return value of a promise then you could wrap the variable inside async function and later use await.
** But async and await is a new feature and not widely supported.
So in your case assuming makeApiRequest() returns a promise
async function any(){ /* using async before function declartion*/
var getValue = await makeApiRequest(); // using await
console.log(getValue); // returned value of makeApiRequest
}

Related

Including rss-to-json feed into a variable [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 years ago.
A very beginner question here.
There's rss-to-json package for npm. This gives the feed in console:
var Feed = require('rss-to-json');
Feed.load('https://learnstartup.net/feed/', function(err, rss){
console.log(rss);
});
But I want the feed in a variable that I can use later like this:
var Feed = require('rss-to-json');
var myfeed = Feed.load('https://learnstartup.net/feed/', function(err, rss){
});
console.log(myfeed);
console.log(myfeed.items[0].title);
Both of the console.logs now give errors. How can I get the json feed in a variable that I can then use? I'm probably doing something wrong with the load function call.
The .load function is not only enclosing variables into a new scope, it's also running asynchronously. What this means is that any code declared immediately after your call to load will actually run before the code within your load callback is run (which will only run after the rss feed is loaded).
What this means is any code that is dependent on the return should therefore be inside the callback function. Like so:
var Feed = require('rss-to-json');
Feed.load('https://learnstartup.net/feed/', function(err, rss){
console.log(rss);
console.log(rss.items[0].title);
});
However, you can see how this nesting can cause readiability issues if you have many nested callback functions. So, depending on your runtime, you can take advantage of the ES6 syntax which introduces the concept of async/await which basically says, "This function is asnychnrous, so wait until it finishes before proceeding." Like so:
var Feed = require('rss-to-json');
(async function() {
var myfeed = await new Promise((resolve, reject) => {
Feed.load('https://learnstartup.net/feed/', function(err, rss){
if (err) { return reject(err) }
resolve(rss)
});
});
console.log(myfeed);
console.log(myfeed.items[0].title);
})();
I should point out, the later option is a little more complicated because the convenient await keyword can only be used inside an async function so I had to wrap the code in an IIFE. Happy to provide more clarity if needed, but if you're new my recommenation is to start with option 1 and consider reading more about these other concepts:
async await
Callbacks vs async await

Return result aSync function in method

First of all, I completely read all the answers on this question, but despite that I come to realize that after years of scripting I ended up in the aSync hell.
I have a method that uses an async function. Depending on the result of that function, the method should return true or false.
So, simply said:
example = {
overview: undefined,
aSyncFunction: function (callback) {
// Adds values to overview, which we will use in otherFunction
callback();
return this;
},
otherFunction: function (data) {
var result = false;
this.aSyncFunction( function () {
var available = this.overview[data.name];
// result == filter overview with supplied data)
}.bind(this));
return result;
}
};
I've created a JsFiddle to show you the exact situation I'm in: https://jsfiddle.net/copt436a/1/
Deleting the setTimeOut will deliver true, otherwise it is false.
Note, I'm cannot change aSyncFunction at the moment, so the solution must be somewhere in otherFunction
I tried to separate the callback function in a different function, but in that case the return value is stuck in that particular function otherFunction keeps returning undefined. Also using the return value of aSyncFunction does not give me the result I want, cause this returns this.
I'm completely stuck on this one, and probably the solution is quite simple.

MarkLogic NodeJS JavaScript function return [duplicate]

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...

Blocking javascript functions (node.js)

I have this code:
var resources = myFunc();
myFunc2(resources);
The problem is that JavaScript calls myFunc() asynchronous, and then myFunc2(), but I don't have the results of myFunc() yet.
Is there a way to block the first call? Or a way to make this work?
The reason why this code doesn't work represents the beauty and pitfalls of async javascript. It doesn't work because it is not supposed to.
When the first line of code is executed, you have basically told node to go do something and let you know when it is done. It then moves on to execute the next line of code - which is why you don't have the response yet when you get here. For more on this, I would study the event-loop in greater detail. It's a bit abstract, but it might help you wrap your head around control flow in node.
This is where callbacks come in. A callback is basically a function you pass to another function that will execute when that second function is complete. The usual signature for a callback is (err, response). This enables you to check for errors and handle them accordingly.
//define first
var first = function ( callback ) {
//This function would do something, then
// when it is done, you callback
// if no error, hand in null
callback(err, res);
};
//Then this is how we call it
first ( function (err, res) {
if ( err ) { return handleError(err); }
//Otherwise do your thing
second(res)
});
As you might imagine, this can get complicated really quickly. It is not uncommon to end up with many nested callbacks which make your code hard to read and debug.
Extra:
If you find yourself in this situation, I would check out the async library. Here is a great tutorial on how to use it.
myFunc(), if asynchronous, needs to accept a callback or return a promise. Typically, you would see something like:
myFunc(function myFuncCallback (resources) {
myFunc2(resources);
});
Without knowing more about your environment and modules, I can't give you specific code. However, most asynchronous functions in Node.js allow you to specify a callback that will be called once the function is complete.
Assuming that myFunc calls some async function, you could do something like this:
function myFunc(callback) {
// do stuff
callSomeAsyncFunction(callback);
}
myFunc(myFunc2);

Chaining Promises recursively

I'm working on a simple Windows 8 app in which I need to fetch a set of data from a web site. I am using WinJS.xhr() to retrieve this data, which returns a Promise. I then pass a callback into this Promise's .then() method, which supplies my callback with the returned value from the asynchronous call. The .then() method returns another Promise, giving it the value that my callback returns. The basic structure of such a query would be as follows:
WinJS.xhr({ url: "http://www.example.com/" }).then(
function callback( result_from_xhr )
{
//do stuff
return some_value;
}).then(
function secondcallback( some_value )
{
//do stuff
});
In my situation, however, I may need to make additional queries for data depending on the data returned by the first query, and possibly more queries depending on THAT data... and so on, recursively.
I need a way to code this such that the final .then() is not executed until ALL the recursions have completed, similar to this:
function recurse() {
return WinJS.xhr({ url: "http://www.example.com/" }).then(
function callback( result_from_xhr )
{
if( result_from_xhr == something )
{
recurse();
}
});
}
recurse().then(
function final()
{
//finishing code
});
The problem is that, of course, the finishing code is called as soon as the first level of recursion completes. I need some way to nest the new promise and the old promise from within the callback.
I hope my question is clear enough, I'm really not sure how to explain it and frankly the idea of asynchronous recursive code makes my head hurt.
What I would do here is to create a whole new, standalone promise that you can complete manually, and return that from the recurse() function. Then, when you hit the point that you know you're done doing async work, complete that promise.
Promise.join works when you've got a known set of promises - you need the entire array of promises available before you call join. If I followed the original question, you have a variable number of promises, with more possibly popping up as part of async work. Join isn't the right tool in these circumstances.
So, what does this look like? Something like this:
function doSomethingAsync() {
return new WinJS.Promise(function (resolve, reject) {
function recurse() {
WinJS.xhr({ url: "http://www.example.com/" })
.then(function onResult(result_from_xhr) {
if (result_from_xhr === something) {
recurse();
} else {
// Done with processing, trigger the final promise
resolve(whateverValue);
},
function onError(err) {
// Fail everything if one of the requests fails, may not be
// the right thing depending on your requirements
reject(err);
});
}
// Kick off the async work
recurse();
});
}
doSomethingAsync().then(
function final()
{
//finishing code
});
I rearranged where the recursion is happening so that we aren't recreating a new promise every time, thus the nested recurse() function instead of having it at the outer level.
I solved this problem in perhaps a different way (I think it's the same problem) while making my own Windows 8 app.
The reason I came across this problem is because, by default, a query to a table will paginate, and only return 50 results at a time, so I created a pattern to get the first 50, and then the next 50, etc, until a response comes back with less than 50 results, and then resolve the promise.
This code isn't going to be real code, just for illustration:
function getAllRows() {
return new WinJS.Promise(function(resolve, reject){
var rows = [];
var recursivelyGetRows = function(skipRows) {
table.skip(skipRows).read()
.then(function(results){
rows = rows.concat(results);
if (results.length < 50) {
resolve(rows);
} else {
recursivelyGetRows(skipRows + 50);
}
})
}
recursivelyGetRows(0);
});
}
so getAllRows() returns a promise that resolves only after we get a result back with less than 50 results (which indicates it's the last page).
Depending on your use case, you'll probably want to throw an error handler in there too.
In case it's unclear, 'table' is a Mobile Services table.
You will need to use Promise.join().done() pattern. Pass an array of Promises to join(), which in your case would be a collection of xhr calls. The join() will only call done() when all of the xhr Promises have completed. You will get an array of results passed to the done(), which you can then iterate over and start again with a new Promise.join().done() call. The thing to be away of, when using this approach, is that if one of the Promises passed to join() fail, the entire operation is treated as an error condition.
Sorry I don't have time right now to try and stub out the code for you. If I get a chance, I will try to later. But you should be able to insert this into your recursive function and get things to work.
Well, I solved my problem; my recursive function was misinterpreting the data and thus never stopped recursing. Thank you for your help, and I'll be sure to watch those screencasts, as I still don't quite fully grasp the Promise chaining structure.

Categories