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.
Related
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
}
I'm having trouble puzzling this resolve function out...
function _resolve(){
var $deferred = $.Deferred();
return $deferred.resolve.apply($deferred, arguments).promise();
}
I see it being used like
if (availableLanguages.length === 1) {
return _resolve(availableLanguages[0]);
}
and
if (detectedLocale && availableLanguages.indexOf(detectedLocale) > -1) {
// save the locale so the user does not get asked for it again
return _resolve(detectedLocale, true);
}
The _resolve function is a shortcut for creating kind of a dummy jQuery Deferred object, which is initially resolved. They should have rather called it for example _createResolvedPromise which would be more intuitive, but we always used to save some characters don't we.
In general this technique is needed when you have a function which returns a deferred, but in some cases you can have an early return with some value. In this case you cannot just return the value, because the caller expects a deferred, so you have to create a deferred object and immediately resolve it with that value. You can perhaps call these deferred objects constant deferred objects or so.
The apply is used to call the resolve with the optional arguments passed to _resolve. Those arguments will be passed to the done callback later on. See the documentation here.
Calling promise() on the deferred object is nothing more than wrapping it into a promise object, so that the consumer cannot call resolve for example. See the documentation here.
For example, let's assume we want to retrieve something via ajax, but also we cache the results.
var _cachedResult;
function getResults() {
if (_cachedResult) {
// for THIS line they made the _resolve shortcut, with which I could write _resolve(_cachedResult)
return $.Deferred().resolve(_cachedResult).promise();
}
return $.ajax(...).done(function(result) {
_cachedResult = result;
});
}
The caller can use it like this.
getResult().done(function(result) {
// do something with result, which maybe came from the cache
});
Hope it makes sense.
In short, it appears to be wrapping up various calls in a Promise/async fashion.
It's likely that detectLocale is an async function, and they're using _resolve to simply handle it in a synchronous manner. Your _resolve function is returning a Promise, which presumably the consumers of those return values are using.
I would expect to see something like this if you trace it up:
function getLanguages() {
if (availableLanguages.length === 1) {
return _resolve(availableLanguages[0]);
}
return new Promise(); // or something, unless it's checking for null outside
}
function doSomething() {
getLanguages().then(languages => console.log(languages));
}
I am new in firebase and I'm trying to pass a $variable in a function to check if the $variable is exists.
function ifExistWaybillNo(waybill_no)
{
var databaseRef = firebase.database().ref('masterlist');
databaseRef.orderByChild("waybill_no").equalTo(waybill_no).on('value', function(snapshot){
alert(snapshot.exists()); //Alert true or false
});
}
The above function work's fine but when I changed alert(snapshot.exists()); to return snapshot.exists(); it doesn't working. It just return undefined, which should return true or false.
How can I do this? thanks in advance
Almost everything Firebase does is asynchronous. When you call the function ifExistWaybillNo it expects an immediate return, not to wait. So before your databaseRef.orderByChild("waybill_no") is finished the statement that called the function has already decided the return is undefined.
The way to fix this is by passing a callback function and using the return there. An exact explanation of this is done very well here: return async call.
You just need to rename some of the functions and follow syntax used there.
To start:
function(waybill_no, callback) {
databaseRef.orderByChild("waybill_no").equalTo(waybill_no).on('value', function(snapshot) {
var truth = snapshot.exists();
callback(truth); // this will "return" your value to the original caller
});
}
Remember, almost everything Firebase is asynchronous.
This question already has answers here:
How do I access previous promise results in a .then() chain?
(17 answers)
Closed 8 years ago.
I need to chain some asynch actions. I'm trying to write Parse.com cloud code which uses express. (I think express is where the promises support originates). I see why promises are valuable, but am still unsure about a couple things. The first is how to collect the results of sequenced actions. The code below illustrates:
function doAsnychThingsInSequence(params) {
return doThing0(params).then(function(resultOfThing0) {
// thing1 depends on resultOfThing0
doThing1(resultOfThing0);
}).then(function(resultOfThing1) {
// here is where I am confused.
// thing2 depends on the results of thing0 and thing1
doThing2(resultOfThing0 /* out of scope?? */, resultOfThing1);
}, function(error) {
// handle error
});
}
After thing1 is done, I need the results of both actions. I think I can allocate a variable at the top of the function and assign it to the first result in the first callback, is that the right way? I guess, at the heart of my confusion is question two...
return doThing0(params0).then(function(resultOfThing0) {
doThing1(resultOfThing0);
// what does return mean here? does what I return here relate to the
// parameters of the next function?
return "foo";
}).then(function(param) {
// what is in param? is it "foo"?
}, function(error) {
});
Promises can be imagined as a stream processing: one function gets input, does something with it and pass it to the next function in the chain.
So if you need to pass input (for the chain) parameters further you should include them into
the output data so next one in the chain can use them:
function doThing1(params1) {
...
return [params1, result1];
}
function doThing2(params1, params2) {
...
}
As you've mentioned you can use some variable outside doThing1 and doThing2 but it will make these functions statefull that may lead to side effects of various kinds. Not desired in general for async processing.
The function supplied to then() must return a value, whether that value is the value to be used, or a promise for an upcoming value. Either way (per the promises spec) then() will return a new promise. That's the first issue I see with your code.
Next is that you have to store thing1 in a higher scope so that you can access it later. So something like this might be in order:
// example
var thing1;
getThing1()
.then(function(value){
thing1 = value; // <-- store in higher scope
return getThing2(); // <-- return
})
.then(function(value){
thing2 = value;
// now you have both thing1 and thing2 in scope
})
I'd like to summarize what I've learned from the two helpful answers from #greim and #c-smile. +1 both for the kind help. But If I'm understanding correctly, then this is a disappointment for me about Promises.
#greim's answer will make my callbacks refer to local variables in the containing function. But this is disappointing because it makes the callback dependent those locals. It would be hard, for example, to pass in a callback.
#c-smile's answer will make my functions return an array (or some collection) of their parameters and their result. All callers of that function would then be expected to dig through the return value for the "natural" result as opposed to the params that it used to get the result.
Restating the answers in terms of my original post:
// #greim
function doAsnychThingsInSequence(params) {
var theResultOfThing0; // yuck, for needing to make up a non-colliding variable name
return doThing0(params).then(function(resultOfThing0) {
theResultOfThing0 = resultOfThing0;
return doThing1(resultOfThing0);
}).then(function(resultOfThing1) {
return doThing2(theResultOfThing0, resultOfThing1);
}, function(error) {
// handle error
});
}
// #c-smile
function doThing1(params) { // params are resultOfThing0
// do stuff
// the "natural" answer is just resultOfThing1, but ...
return [params, resultOfThing1];
}
// if this function was doThingN, then we'd have
return [params0, params1 ... paramsN, resultOfThingN]; // yikes!
// at least this method looks great now, how I want it to look...
function doAsnychThingsInSequence(params) {
return doThing0(params).then(function(resultOfThing0) {
return doThing1(resultOfThing0);
}).then(function(resultOfThing0, resultOfThing1) {
return doThing2(resultOfThing0, resultOfThing1);
}, function(error) {
// handle error
});
}
tl;dr : I'm looking for a way to have the first .then callback make changes to the data that is passed to subsequent chained events.
I have a library that encapsulates some async operations.
dudetools.getDude(2); // causes an XHR against REST resource "Dude" for row id 2
For awesomeness purposes, dudetools.getDude returns the promise created by the underlying $.ajax call. Thus, I can do things like:
dudetools.getDude(dudeId).done(function(dudeData) { /* do stuff with dude's data */ });
Now I'm trying to modify dudetools so that it'll do some convenient data-massaging on response data before continuing along the promise chain. I want this massage to happen universally, without calling code having to request it or even know about it.
Because the dudetools implementation can't share a closure with all calling code, I'm hoping to leverage the fact that, in JavaScript, non-scalars are always passed by reference rather than by value.
Consider:
var urStuff = {};
function wreck(x) {
x.isWrecked = 'so wrecked';
}
wreck(urStuff);
// urStuff.isWrecked === 'so wrecked' ^.^
I dare you to try it.
So, I was hoping this would work:
dudetools = {
'getDude': function(dudeId) {
return $.ajax('/api/Dude/' + dudeId).then(function(dudeData) {
// I'm so clever!
dudeData.isDuplicated = dudeData.isDuped && dudeData.drillDown > 5;
});
}
}
Of course, it doesn't work. My clever code is being executed (I've seen it), and it's reaching the right conclusions, but subsequent Deferred events in the chain never see the modifications. I.e.:
$.when(
dudetools.getDude(dudeId)
).done(function(mysteriouslyUnmodifiedInfo) {
/* the info passed to this function is mysteriously unmodified! HALP */
});
Any suggestions? Is there a way to accomplish what I'm after?
Also: I'm still kind of new to promises in general, and my grasp of the differences between Deferreds, Promises, and associated constructs is still kind of fuzzy, so I'd very much appreciate your efforts to be clear and explicit when explaining to me how I've ruined everything.
Thanks very much.
EDIT: updated to reflect fact that dudetools.getDude returns a promise, not a Deferred. Because I now (mostly) understand the difference.
The magic of .then is that it pipes its return value into the next callbacks param.
If you don't return your object (even if you haven't changed anything), then undefined is returned by default.
do_something()
.then(function (json) { return JSON.parse(json); })
.then(function (response) { return response.data; })
.then(function (data) { data.tweaked = true; return data; });
You'll want to return your own new Deferred.promise() object.
http://api.jquery.com/deferred.promise/
dudetools = {
'getDude': function(dudeId) {
var dfd = new jQuery.Deferred();
$.ajax('/api/Dude/' + dudeId).then(function(dudeData) {
dudeData.isDuplicated = dudeData.isDuped && dudeData.drillDown > 5;
// I'm so clever!
dfd.resolve(dudeData);
});
return dfd.promise();
}
}
Hope that helps.