I'm pretty new to Javascript, and I don't understand why I can't save the result of this code below.
I'm working with MongoDB and I want to store the number of documents in a cluster so that I can keep track of how many are in the cluster, however, when I try to log the output on the third line, it is undefined. Why is this?
let theIndex;
collection.countDocuments({}).then(res => theIndex = res);
console.log(theIndex);
collection.countDocuments({}) returns a promise so you should use console.log once it resolves:
collection.countDocuments({}).then(res => {
theIndex = res;
console.log(theIndex);
});
The callback function inside .then is executed once the promise resolves. Logging it outside the callback won't work because that console.log line gets executed immediately and at a later point of time(because reaching the database and such takes time) the callback is called.
Note that even if the promise resolves immediately(as fast as possible), the callback still gets executed later because of the event loop in javascript.
Related
I am new to promises and have studied it. So my code and my understanding:
sql.connect(config).then(function(connection) {
return connection.request().query('select * from Users')
}).then(function(result) {
console.dir(result);
res.send(result);
}).catch((err) => {
console.log(err);
res.send(err)
});
}) // extra?
In the first line, the connect method returns a promise so on that, when I call a function then (if connect had success). The then takes callbacks i.e. "successCB", "failureCB" as arguments which actually represent the result or error returned by the connect promise. Correct?
Also, the "successCB" is returning the promise (from .query).
then is again called on the previous promise returned by then.
In case of then used on connect() why is the then taking a callback and how does it know of connect's success since connect has already returned the result i.e. "successCB"?
I'm not 100% certain if you are correct, but this is how I would explain it. Lets say we have a promise A.
Calling then() always returns a new promise (Lets call it B)
If the callback returns a promise C, then promise B will resolve or fail with the result of promise C.
If any of the callbacks returns a non-promise value X, then promise B will resolve with value X
If an exception was thrown in the callback, then promise B will fail with the exception.
If the main promise A failed, and no failure callback was given, then promise B will also fail with the same error.
Personally I found learning this very confusing, because it requires quite a complex mental model to fully grasp this. The day I fully understood this is the day that I wrote my own Promise class. I would recommend anyone who fully wants to grasp promises in every detail to take the time to do this.
In the first line, the connect method returns a promise so on that, I call a function then (if connect had success).
Almost: the then method is called irrespective of the connect promise's result. Note that the promise is most likely still pending when the then method is called. It is called (obviously) synchronously.
The then takes callbacks i.e. successCB, failureCB as an args which actually represents the result or error returned by the connect promise. Correct?
Those callbacks are registered by the promise object, and one of them will be called later when the promise resolves: it can either fulfil or reject. If the promise never resolves, neither will be called. In your case, you provided just one function, the one that will be called when the promise fulfils.
Also, the successCB is returning the promise (from .query).
True. The promise that was created when the outer then was executed will take over the status/value of the promise that was returned here.
then is again called on the previous promise returned by then.
Yes, but again, it is called immediately. All chained then/catch methods are called synchronously and their job is to register the callbacks and to return a promise.
The asynchronous part is when the respective callbacks are called. This is when the cascading effect takes place. Once the first promise (query()) in the chain resolves, the respective callback is called and that call resolves (fulfils or rejects) the promise that was already returned by then, which triggers a next callback... etc
Order of execution
Part A
sql.connect(config)
calls connect and returns a promise
.then(......)
calls then on the promise of A.1, registers the callback and returns a promise
.catch(.....)
calls catch on the promise of A.2, registers the callback and returns a promise.
This happens to be the last one in the chain, so this promise is the value of the whole expression.
At this point the synchronous part ends. If there is other calling code on the callstack, it gets executed, but eventually the call stack will be empty.
Part B
Then, some time later, there is an event indicating that the promise of A.1 is resolved, let's assume it fulfils (i.e. success). Then the first callback is executed:
connection.request() is executed
it calls request which returns an object with the query method (among others)
.query('select * from Users') is executed
it calls query with the SQL and returns a promise
return is executed
the callback returns the promise of B.2, which becomes linked with the promise that was created in step A.1.
At this point the promise of step A.1, is still pending. Its destiny is tied up now with the promise that was returned by query, B.2. It that one resolves, A.1 will also resolve (with the same state & value/error).
Part C
Then, some time later, there is an event indicating that the promise of step A.1 is resolved, let's assume it fulfils. Then the second callback is executed:
console.dir(result) is executed
res.send(result) is executed
Nothing is returned, so the promise of 1.B fulfils with value undefined
This fulfilment creates a next asynchronous job to execute (part D)
Part D
The promise returned by catch (A.3) resolves with the value with which C.3 resolved, i.e. it fulfils with value undefined. The callback it had registered is not executed, as it is not relevant for this scenario.
Thanks for replies and I have already selected the answer. Based on the input given by you people this is what I have understood further.
sql.connect(config).then(function(connection) {
return connection.request().query('select * from Users')
})
In the code given above, sql.connect part is using an SQL module to call a connect() method which is supposed to return a promise (in case of failure returns an error else a connection object). On connect() we call a then() method which takes a callback function function(connection) to get registered so that can be called later at the point where connect returns a promise (in case of success i.e. connection object) and later
.then(function(result) {
console.dir(result);
res.send(result);
}).
is called on the promise returned by .query('select * from Users') part of the previous .then() method's callback function and so on.
i have this code:
let splatshArtData = [];
splatshArt.getSplatchArt(participants[i].championId).then((splatshArtUrl) => {
splatshArtData.push(splatshArtUrl);
});
console.log(splatshArtData);
I want add the "splatshArtUrl" to my array, but this don't work, when i try print the data this don't print nothing, i don't know how to do that, any idea?
The problem that you're facing here is that getSplatchArt returns a promise, and promises take time to resolve. You can therefore never guarantee that splatshArtData.push(splatshArtUrl); will run before the console.log.
The solution is to move all logic which requires data returned from the promise to within the promise callback. This can of course include calls to other functions.
// function to process the splashArtData - will be called from the promise
// when the promise is resolved.
function processSplashArt(data) {
// this will now print as you are expecting
console.log(data);
}
let splatshArtData = [];
splatshArt.getSplatchArt(participants[i].championId).then((splatshArtUrl) => {
splatshArtData.push(splatshArtUrl);
// pass the splashArtData to the callback function - it's now ready
processSplashArt(slashArtData);
});
JavaScript is synchronous, thus each line of code will be executed right after one another.
If we annotate your code with line numbers like below
1. let splatshArtData = [];
2. splatshArt.getSplatchArt(participants[i].championId).then((splatshArtUrl) => {
3. splatshArtData.push(splatshArtUrl);
});
4. console.log(splatshArtData);
you are assuming that it will run in the order of 1, 2, 3, 4, while in reality it will run in the order of 1, 2, 4, 3. Why is that? Because JavaScript is synchronous, and the function at line 2 is asynchronous, meaning that you'll have to await that before you can continue. If you don't the splatshArtData variable will be an empty array since the data hasn't been fetched yet.
If you want to return the fetched data and use it in another function, you shouldn't mix it will callbacks as suggested in another answer, but instead chain the promise and use the resolved value from the function that fetches the data.
function getSplatshArt() {
let splatshArtData = [];
//Return a promise
return splatshArt.getSplatchArt(participants[i].championId).then((splatshArtUrl) => {
console.log(splatshArtData); //this will log out the data
splatshArtData.push(splatshArtUrl);
return splatshArtData; //this will be the resolved value from the promise
});
}
//Call the function and access the resolved data in the .then() block
getSplatshArt()
.then(data => {
console.log(data); //this is the data returned from the getSplatshArt function
});
Looking at your code, I get the impression that you're looping over an array of ID's, and if you want to fetch multiple values at once, this wont work since you have to handle multiple promises. But that is another question and I think you should do some more research of your own before asking about that.
Try this:
let splatshArtData = [];
splatshArt.getSplatchArt(participants[i].championId).then((splatshArtUrl) => {
splatshArtData.push(splatshArtUrl);
console.log(splatshArtData);
});
The function inside the then runs right after the asynchronous function getSplatchArt resolves it's promise, so it was running the console.log before the item is added to the array.
I'm having trouble understanding how Node operates regarding it's parallel processing and returning values from function calls.
FYI: The gulp function below is merely created as an example for this question.
Is it possible that the function could return the stream before the Read a large file statement has finished processing (the large file has been fully read from the file system and the stream has been added), or is Node smart enough to complete all statements before returning?
function moveFiles(){
var gulp = require('gulp'),
stream = require('merge-stream')();
// Read a large file
stream.add(gulp.src('src/large-file.txt')
.pipe(gulp.dest('dest/'))
);
// Read a small file
stream.add(gulp.src('src/small-file.txt')
.pipe(gulp.dest('dest/'))
);
return (stream.isEmpty() ? null : stream);
}
Could Node feasibly return a value from a function call before completing all operations within the function itself?
This is a tricky question. The answer is no, in a way that returning a value means that the function is finished executing, it's taken back from the stack and it will never do anything again - unless it's invoked another time of course, but the point is that this particular invocation is over.
But the tricky part is that it's the function that's finished executing and it doesn't mean that it couldn't schedule something else to happen in the future. It will get more complicated in a minute but first a very simple example.
function x() {
setTimeout(function () {
console.log('x1'));
}, 2000);
console.log('x2');
return;
console.log('x3');
}
Here when you call x() then it will schedule another function to run after 2 seconds, then it will print x2 and then it will return - at which point this function cannot do anything else ever again for that invocation.
It means that x3 will never get printed, but x1 will eventually get printed - because it's another function that will be called when the timeout fires. The anonymous function will get called not because the x() function can do anything after it returns, but because it managed to schedule the timeout before it returned.
Now, instead of just scheduling things to happen in the future, a function can return a promise that will get resolved some time later. For example:
function y() {
console.log('y1');
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve('message from y()');
}, 2000);
});
console.log('y2');
}
Now, when you run:
var promise = y();
what will happen is that y1 will get printed, a new promise will get returned and y2 will never get printed because at that point y() returned and cannot do anything else. But it managed to schedule a timeout that will resolve the promise after two seconds.
You can observe it with:
promise.then(function (value) {
console.log(value);
});
So with this example you can see that while the y() function itself returned and cannot do anything else, some other (anonymous in this case) function can be called in the future and finish the job that the y() function has initiated.
So I hope now it's clear why it's a tricky question. In a way a function cannot do anything after returning. But it could have scheduled some other functions as timeouts, event handlers etc. that can do something after the functions returns. And if the thing that the function returns is a promise then the caller can easily observe the value in the future when it's ready.
All of the examples could be simplified by using the arrow functions but I wanted to make it explicit that those are all separate functions, some of them are named, some are anonymous.
For more details see some of those answers:
A detailed explanation on how to use callbacks and promises
Explanation on how to use promises in complex request handlers
An explanation of what a promise really is, on the example of AJAX requests
An explanation of callbacks, promises and how to access data returned asynchronously
I'm trying to execute an asynchronous routine for a bunch of items in a list that I get from a database, but I'm having trouble understanding how promise.all works and what it does.
Here is the code I'm using right now:
/**
* Queues up price updates
*/
function updatePrices() {
console.log("~~~ Now updating all listing prices from Amazon API ~~~");
//Grabs the listings from the database, this part works fine
fetchListings().then(function(listings) {
//Creates an array of promises from my listing helper class
Promise.all(listings.map(function(listing){
//The promise that resolves to a response from the routine
return(listing_helper.listingPriceUpdateRoutine(listing.asin));
})).then(function(results){
//We want to log the result of all the routine responses
results.map(function(result){
console.log(result);
});
//Let us know everything finished
console.log("~~~ Listings updated ~~~");
}).catch(function(err){
console.log("Catch: ", err);
});
});
}
Right now, the only thing I get in the log is
~~~ Now updating all listing prices from Amazon API ~~~
I've tried adding a logging piece into the routine that is called and the routines all run successfully and log what they should, but promise.all.then doesn't execute.
I've tried just doing:
Promise.all(bleh).then(console.log("We did it"));
and that worked, but when I put a function in the Then, nothing runs.
Please help!
Promise.all() itself is pretty simple. You pass it an array of promises. It returns a new promise that will resolve when all the promises in your array resolve or will reject when any individual promise in the array rejects.
var pAll = Promise.all([p1, p2, p3]);
pAll.then(function(r) {
// all promises resolved
// r is an array of results
}, function(err) {
// one or more promises rejected
// err is the reason for the first promise that rejected
});
Some reasons that Promise.all() might not work in your code:
You aren't passing an array of promises to it.
Some of the promises in the array you pass never resolve or reject thus Promise.all() never resolves/rejects its master promise
You aren't properly passing callbacks to .then() handlers where you should be
You aren't properly returning promises from internal functions so they propagate out or chain properly
Your example:
Promise.all(bleh).then(console.log("We did it"));
Is wrong. You must pass a function reference to .then() like this:
Promise.all(bleh).then(function() {
console.log("We did it")
});
In your case, the console.log() would execute immediately and not wait for the promises to be resolved.
In your detailed code are you 100% sure that:
listing_helper.listingPriceUpdateRoutine(listing.asin)
is returning a promise? And, that that promise will get resolved or rejected properly?
Note to Readers - If you read all the comments, you can see that the OP's actual issue was not with Promise.all(), but they were getting rate limited for sending requests too quickly to the target host. Since that should have been propagating a request error back which should have been easily visible, the OP apparently also has a problem with error handling or propagation which is likely in code that was not disclosed here.
I have a pretty silly problem. Consider the following:
vm.feed = getFeed().then(function(data) {return data;});
getFeed() returns a $q deferred promise (I am on angular) that resolves successfully.
My goal is to set vm.feed equal to the data value returned by the successful callback. As it is right now, the code simply assigns vm.feed equal to the $promise object returned by getFeed().
I know I could simply do: vm.feed = data inside the resolved function but I want to understand why this code does not work as it is.
PD: the promise resolves correctly and even after it has been resolved vm.feed keeps being equal to the Promise, and not data. I copy the console.log of vm.feed after +10 seconds have elapsed:
Promise {$$state: Object} $$state: Objectstatus:1 value: Object
That value property inside the Promise object contains the actual solution of the promise that I want to assign to vm.feed (e.i. data).
thank you!
This could be updated to ES6 with arrow functions and look like:
getFeed().then(data => (vm.feed = data));
If you wish to use the async function, you could also do like that:
async function myFunction(){
vm.feed = await getFeed();
// do whatever you need with vm.feed below
}
Edit: added parenthesis to be eslint correct (as commented by Max Waterman)
You are going to get whatever then() returns. But since you are reading this, the following may help you:
Your statement does nothing more than ask the interpreter to assign the value returned from then() to the vm.feed variable. then() returns you a Promise (as you can see here: https://github.com/angular/angular.js/blob/ce77c25b067b7b74d90de23bfb4aac6a27abb9d1/src/ng/q.js#L288). You could picture this by seeing that the Promise (a simple object) is being pulled out of the function and getting assigned to vm.feed. This happens as soon as the interpreter executes that line.
Since your successful callback does not run when you call then() but only when your promise gets resolved (at a later time, asynchronously) it would be impossible for then() to return its value for the caller. This is the default way Javascript works. This was the exact reason Promises were introduced, so you could ask the interpreter to push the value to you, in the form of a callback.
Though on a future version that is being worked on for JavaScript (ES2016) a couple keywords will be introduced to work pretty much as you are expecting right now. The good news is you can start writing code like this today through transpilation from ES2016 to the current widely supported version (ES5).
A nice introduction to the topic is available at: https://www.youtube.com/watch?v=lil4YCCXRYc
To use it right now you can transpile your code through Babel: https://babeljs.io/docs/usage/experimental/ (by running with --stage 1).
You can also see some examples here: https://github.com/lukehoban/ecmascript-asyncawait.
The then() method returns a Promise. It takes two arguments, both are callback functions for the success and failure cases of the Promise. the promise object itself doesn't give you the resolved data directly, the interface of this object only provides the data via callbacks supplied. So, you have to do this like this:
getFeed().then(function(data) { vm.feed = data;});
The then() function returns the promise with a resolved value of the previous then() callback, allowing you the pass the value to subsequent callbacks:
promiseB = promiseA.then(function(result) {
return result + 1;
});
// promiseB will be resolved immediately after promiseA is resolved
// and its value will be the result of promiseA incremented by 1
You could provide your function with the object and its attribute. Next, do what you need to do inside the function. Finally, assign the value returned in the promise to the right place in your object. Here's an example:
let myFunction = function (vm, feed) {
getFeed().then( data => {
vm[feed] = data
})
}
myFunction(vm, "feed")
You can also write a self-invoking function if you want.
This is one "trick" you can do since your out of an async function so can't use await keywork
Do what you want to do with vm.feed inside a setTimeout
vm.feed = getFeed().then(function(data) {return data;});
setTimeout(() => {
// do you stuf here
// after the time you promise will be revolved or rejected
// if you need some of the values in here immediately out of settimeout
// might occur an error if promise wore not yet resolved or rejected
console.log("vm.feed",vm.feed);
}, 100);