In this example from Angularjs Docs there is some *magic*, which i can't figure out.
Here is a code:
var User = $resource('/user/:userId', {userId:'#id'});
var user = User.get({userId:123}, function() {
user.abc = true;
user.$save();
});
And one thing confusing me - how we can refer to user object inside callback and get retrieved data, when it is necessary to populate user first. But to do so value should be returned from User.get().
Like that:
call User.get() → return from User.get() → call function() callback
But after return it is not possible to execute anything, right?
It is absolutely possible to return the user and still have not executed the callback, and this is what this code example is showing.
The implementation of User.get might be something like:
User.prototype.get = function(parameters, callback) {
var user = {
abc: false
}
// do other stuff to construct the object
someLongAsyncOperation(callback);
return user;
}
Here the user is returned, but there is still a callback to be completed. Then within that callback the user can be accessed via closure scope. someLongAsyncOperation could be anything that later calls callback, you could try it out yourself with something as simple as a setTimeout or Angular's $timeout.
Its not in my opinion a great pattern, and it might be better to include the created user as a parameter in the callback - but it would work.
User.get() returns a blank object and performs an async GET to /user/123. This empty object is assigned to the user variable in the function scope.
The rest of the function (if there is any) executes and the function returns.
When the async call returns, the get() method populates the existing blank object's properties with the properties of the object returned, so the user variable now has the results of the callback.
The get() method calls the callback you specify. It has access to the function's user variable that is now populated from the async call.
Related
I want to call an endpoint using Axios, then do something with the response data and a variable that was defined before the call. I know how to pass data into the callback when creating the function returning the promise myself, but since Axios is external code and I can't seem to find any option to pass extra data in the Axios docs or the config. Is there any other way to make this work, other than sending the value as data to the server and have the server respond it within the response or using an ugly workaround using the window global variable?
const animate = true; // The variable to be passed
axios.get('/endpoint.json')
.then(function(response, animate) { // This doesn't work...
if (response.data.coordinates) {
console.log(animate); // ...because this is undefined
setLocation('takeoff_location', response.data.coordinates, animate); // variable and response data need to be passed to this call
}
});
This creates a new animate variable, entirely unrelated to the one already defined:
function(response, animate)
This new variable "shadows" the one in the higher scope, so within this function any reference to animate only references this new variable. Which isn't defined because Axios (specifically the Promise returned by .get()) has no reason to pass a second value to this callback.
Don't shadow the variable:
function(response)
Then within the function any references to animate will reference the variable in the higher scope.
the .then() is a promise and has access to any variables outside the scope (like a closure). If you want to encapsulate the API call then wrap the entire thing in a function and the arguments go there.
const doRequest = function(animate) {
axios.get('/endpoint.json').then(function(response) {
if (response.data.coordinates) {
setLocation('takeoff_location', response.data.coordinates, animate);
}
});
};
doRequest(250);
doRequest(500);
Note that this will do 2 animation requests quickly in succession. If you want to "wait" for the other one to finish first, you would do this: (note that I'm now returning the axios request from the function). This means the promise object is returned and now you can chain more .then() functions on the "outside" of the function, which will happen only when the request finishes.
const doRequest = function(animate) {
return axios.get('/endpoint.json').then(function(response) {
if (response.data.coordinates) {
setLocation('takeoff_location', response.data.coordinates, animate);
}
});
};
doRequest(250).then(function() {
// this will happen when the outer API call finishes
doRequest(500);
});
I have been going in circles trying to deeply understand JS promises and I can't seem to find a simple answer on how in the world does it passes the value of the resolved to the handle success variable, I don't understand is that as a build method? But the name is (resolvedValue)
For example, I saw also in another example that they used (rejectionReason) to handle the rejected value, I want to understand how that happens.
here is the code:
const prom = new Promise((resolve, reject) => {
resolve('Yay!');
});
// this resolvedValue how does it happen?
const handleSuccess = (resolvedValue) => {
console.log(resolvedValue);
};
prom.then(handleSuccess); // Prints: 'Yay!'
First lets clarify on two different concepts:
Function Definition:
const exampleFunction=(name)=>{console.log(name)} // just defined, does nothing
Function Invocation:
exampleFunction("Erfan") // logs "Erfan"
In JavaScript, function definitions can be passed to another function ("a higher order function") as a parameter, usually we describe such situation as "the function taking a callback".
The higher order function then calls the callback according to its own implementation, and may or may not pass a value to it.
In your example, method then of the the prom is the higher order function. it takes a callback, passes the resolve value to it, and calls it. it doesn't care about its name, just looks for a function definition and calls it.
Credits to got to #Nirjal Paudel reference link, the answer to my question is :
it happens by paassing it in the value as an anonymous function.
Promises have a method called then that will run after a promise reaches resolve in the code. then will return the promise’s value as a parameter.
This is how you would return and log the value of the example promise:
const handleSuccess = (resolvedValue) => {
console.log(resolvedValue);
};
prom.then(handleSuccess);
The promise you created had a [[PromiseValue]] of 'Yay!'. This value is what will be passed into the anonymous function as a response
so this means that the name won't matter
I have a question about asynchronous/synchronous when we write code in JS and I have done google search but I am still a bit confused.
I understand that we use callback functions when we want to make sure that the callback function is executed only after the asynchronous task (such as accessing database) in the outer function is complete. I understand how deserialize and serialize user works.
I am confused why when we do serialize user or deserialize user with passport.js we need a callback function like this ?
passport.serializeUser((user, done) => {
done(null, user.id);
});
If all we want is that the inner arrow function that is passed as an argument to serializeUser() to be executed only after serializeUser() is finished. Or why do we need to pass it as a callback function instead of calling that arrow function below serializeUser() ? I thought JS is synchronous so it will execute the arrow function after serializeUser() is completed anyway ?
I only found serializeUser() documentation in passport documentation on how to use it, but not its implementation so I am also confused whether serializeUser() or deserializeUser()( or any other passport functions) are asynchronous functions ?
Thank you !
It is a fragment of this function on github (https://github.com/jaredhanson/passport/blob/08f57c2e3086955f06f42d9ac7ad466d1f10019c/lib/authenticator.js).
As you can see this function takes a fn parameter. Then it checks if it is a function. If yes it pushes it to this._serializers. Later it does 'some magic' on it, I think it's not important now.
As you can read in jsdoc, serializeUser purpose is to register a function used to serialize user objects. So you pass some function which is invoked later in a code with 2 arguments which labels on that particular function level are user and done.
They left you some space which you can fill with your own code. You can tell how this will behave. They gave you an opportunity to implement your own logic.
Simple example:
function doSomeMagic(fn) {
// I do some my staff
const a = 5;
const b = 10;
// and now I let you decide what to do
// you can implement your own logic in my function
const result = fn(a, b);
console.log(result);
}
doSomeMagic((a, b) => {
return a * b;
});
doSomeMagic((a, b) => {
return a + b;
});
//output:
// 50
// 15
That's exactly what they've done. You can take my function, I give you two arguments do whatever you want. You can multiply, add, substract, whatever you want. I don't have to write separated functions to do math, I can write one, pass values as arguments and let you decided what operation you want to do. And it doesn't mean my code is going to run in an async way.
I am using an ID3 reader script to retrieve data from audio files. The basic usage is:
ID3.loadTags(file,function()
{
var attr = ID3.getAllTags(file).attribute;
});
where the anonymous function is a callback function. This is just to provide context, however, I'm not at all sure the problem I'm having is specific to that particular script.
Typically, inside the callback function, you can extract the info you need and then use the DOM to set the innerHTML attribute of whatever element to equal the info you extracted.
Sometimes you're extracting a bunch of info and connecting all together in a long string though, and I'm trying to compartmentalize it a bit more so that my calling function will be a little cleaner. What I want to do is this:
function callingFunction()
{
var file = "whatever.mp3";
var info = getInfo(file);
}
function calledFunction(file)
{
var info = {data: 0};
ID3.loadTags(file, function(passedVar)
{
var dataobj = ID3.getAllTags(file);
passedVar.data = dataobj.title+etc+dataobj.album+....(it can get long);
}(info));
return info;
}
An object with an attribute is created because it's one of the only ways to simulate passing by reference in JS - pass the object into the callback function, assign the appropriate data to the attribute in the object, and then at the end of calledFunction, return the object to callingFunction.
It doesn't work though. Now, in that code above, if I said passedVar.data = "teststring" instead of assigning it data from dataobj, that would work, so the passing of the object into the callback function is working correctly. But if the object is assigned data from the data object that the ID3 function returns, it doesn't work. It comes back undefined, and furthermore, Chrome's JS debugger says that the object the ID3 function returns is null. This is further confirmed when I do this:
function calledFunction(file)
{
var info = {data: 0};
ID3.loadTags(file, function(passedVar)
{
alert(ID3.getAllTags(file).(any attribute));
}(info));
return info;
}
and no alert box comes up at all. But if remove the parameter being passed into the callback function in the code above, and leave everything else the same, that alert box comes up like it's supposed to.
So, to sum up, when I pass a parameter into the callback function, for some reason, another function of the object that's calling the callback function ceases to work correctly.
Is it possible that passing a parameter into the callback function is somehow conflicting with, or overriding, whatever the ID3.loadTags function is passing into the callback function, and that's why the getAllTags function is failing? Because for some reason when a parameter is passed into the callback function, the getAllTags function is no longer getting all of the information it needs to run properly? That's the only explanation I can think of.
If that's the case, is there a way to work around it? And if that's not what's going on, what is going on?
I have figured one solution, but I feel like it's hacky. I basically create a third function that gets called from the callback function(which itself receives no parameters), that takes as a parameter the object that the getAllTags method returns, extracts data from that object, and assigns it to global variables that other functions can access. So, this:
var globalVar;
function calledFunction(file)
{
//var info = {data: 0};
ID3.loadTags(file, function()
{
thirdFunction(ID3.getAllTags(file));
});
//return info;
}
function thirdFunction(dataobj)
{
globalVar = dataobj.title+etc;
}
But I don't really like that solution, I feel like it goes against the spirit of compartmentalization that I was after in the first place with this.
I'd appreciate any help.
The reason this doesn't work:
function calledFunction(file)
{
var info = {data: 0};
ID3.loadTags(file, function(passedVar)
{
var dataobj = ID3.getAllTags(file);
passedVar.data = dataobj.title+etc+dataobj.album+....(it can get long);
} (info));
// ^^^^^^ --- calls the function immediately
return info;
}
...is that you're calling your anonymous function and passing the result of that call (undefined) into ID3.loadTags. You're not passing a function into it anymore at all.
But the fundamental problem is that you're trying to use the data object before loadTags calls its callback and puts the data on the object.
I suggest that, since its output depends on an asynchronous operation, rather than relying on function return values, you change calledFunction to take a callback function. Here's what it should look like:
function callingFunction() {
getInfo('whatever.mp3', function(info) { // pass a callback function
// info.data is here now
});
}
function getInfo(file, cb) { // accept a callback function as the 2nd param
ID3.loadTags(file, function() {
var tags = ID3.getAllTags(file);
// once your async operation is done, call cb and pass back the return value
cb({
data: tags.title+etc+tags.album+....(it can get long);
});
});
}
This approach avoids problems you were trying to solve by using an object you could pass by reference, and it ensures you only move on once your asynchronous operation (ID3.getAllTags) is complete.
See the following line of code:
WinJS.xhr({ url: "http://someurl.com" }).then(
function fulfilled(result)
{
if (result.status === 200)
{
resDiv.style.backgroundColor = "lightGreen";
resDiv.innerText = "Success";
}
});
As far as I understand, when WinJS.xhr has completed whatever it does then execute the anonymous function 'fulfilled' with argument 'result'
Coming from Java/C++ background, I'm extremely confused with how this code works - how is 'result' being passed to this function? Where does it say anything about what 'result' is? How can I know what type of object 'result' is and how it has a 'status' member?
I'm going to break my answer into two parts: The first concerns the actual execution model of Javascript, and the second which concerns the high-level expression as written.
The Javascript Execution Model
WinJS evaluates to an object.
That object has a prototype which contains an xhr member which WinJS.xhr evaluates to. That member is a function, which we will refer to as A below so that we can keep clear what exactly is going on.
Before we get to that, { url: "http://someurl.com" } returns an object which we will refer to as B.
That object B has a property called url.
A(B) calls a function A with a value B as an argument. It returns an object that we will refer to as C.
That object C has a prototype which contains a member named then. C.then happens to evaluate to a function. That function we will refer to as D.
function fulfilled(result) {...} returns a function that we will refer to as E. It can also be referred to as fulfilled but that fact is not used in this program fragment.
D(E) calls a function D with a value E as an argument. Nothing is done with the return value.
The high-level view
There are three functions here; one is a callback (called fulfilled), and the other two may be called "methods"- one xhr of the WinJS global object, and then of a promise object.
WinJS.xhr({ url: "http://someurl.com" }) creates and returns that promise object. You can convince yourself of this by consulting the documentation.
The promise object has a method called then which registers what you can think of as an event handler for when the promise is done. The value result - used in that callback registered in then comes from whatever is making that promise done by in fact calling the method done on that promise. You don't see the code that does that because it's someplace in the implementation of WinJS.xhr.
What WinJS.xhr is doing is performing a network request. When that network request is done it will signal the result of that network request (which according to the documentation is an XMLHttpRequest object) through the promise by calling the done() method on that promise. That in-turn calls the callback we registered with the then() method.
The documentation for the WinJS functions is still not great, IMO. You can look at the documentation for WinJS.xhr at http://msdn.microsoft.com/en-us/library/windows/apps/br229787.aspx, and that will give you some information on that - it says that the xhr function "wraps ... XMLHttpRequest object in a promise". Personally I've found it easier to look at the examples / quickstarts than at the reference documentations.
What you have passed to the fulfilled function (or the first function there) isn't the "result" of the operation, but the XMLHttpRequest object itself. On that you can get its properties to see the result - take a look at http://msdn.microsoft.com/en-us/library/windows/apps/ms535874.aspx for its reference.
The xhr method returns a Promise object, which has the then method that takes the onComplete, onError and onProcress callback functions.
This code only uses the onComplete callback. The callback is called when the request is completed, and it's sent a parameter with the value sent from the server.
If you are not familiar will callback functions, it may be clearer if you declare a regular function an use as the callback:
function fulfilled(result) {
if (result.status === 200) {
resDiv.style.backgroundColor = "lightGreen";
resDiv.innerText = "Success";
}
}
WinJS.xhr({ url: "http://someurl.com" }).then(fulfilled);
The then method is intended for pre-processing the result, you should rather use the done method to take care of the result.
I haven't found anything in the documentation that specifies exactly in what form the value comes from the server. It's probably there somewhere, but as usual with Microsoft documentation, it's rather complete but everything isn't everywhere so you have to look in different places to find specific information.