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
Related
I am using Binance's Node.js API. It says regarding "Get open orders for a symbol", I should do:
binance.openOrders("ETHBTC", (error, openOrders, symbol) => {
console.info("openOrders("+symbol+")", openOrders);
});
To print out number of open orders, I do:
binance.openOrders("ETHBTC", (error, openOrders, symbol) => {
console.info(openOrders.length);
});
which works and the number gets printed out. However, I would need this result to be stored in a variable which can be used later by other functions. Building on SO's Javascript chat room, I do:
let OO =
(async() => {
const openOrders = await binance.openOrders(false);
return openOrders.length
})()
console.log(OO)
This however prints
Promise { <pending> }
only.
I have seen several other questions discussing Promise { <pending> } issue but I haven't been able to implement their solutions to this specific case.
How could I get number of open orders into a variable accessible by other functions?
You'll need to use either completely async approach or use callbacks.
The last block in your question shows exactly what this answer explains. Javascript doesn't wait for Promise to resolve/reject in a synchronous context. So your "async" block returned the unresolved Promise and the rest of your (synchronous) code didn't wait for it to resolve.
Example of using async functions
const getOpenOrdersCount = async () => {
const openOrders = await binance.openOrders("ETHBTC");
return openOrders.length;
};
const run = async () => {
const openOrdersCount = await getOpenOrdersCount();
console.log(openOrdersCount);
}
Note: You can use await only within async functions.
Example of using callbacks is your code. They are useful in a small scope, but in bigger scope they get messy and turn into a callback hell. So I wouldn't recommend using callbacks in a bigger scope.
binance.openOrders("ETHBTC", (error, openOrders, symbol) => {
console.info(openOrders.length);
// here goes rest of your code that needs to use the `openOrders` variable
});
This question already has answers here:
Will async/await block a thread node.js
(6 answers)
In JavaScript, does using await inside a loop block the loop?
(8 answers)
Closed 4 years ago.
I was reading Don't Block the Event Loop from the Node.js guide. There was a line saying:
You should make sure you never block the Event Loop. In other words,
each of your JavaScript callbacks should complete quickly. This of
course also applies to your await's, your Promise.then's, and so on.
I started to wonder, what if, some API call to the database which I'm awaiting is taking some time to resolve, does that mean that I have blocked the event loop with that await call?
After that, I started testing some self written codes but after testing I'm still not clear how blocking through await works. Here are some testing codes:
Assuming, that I'm using express for testing. I understand why making 2 API calls to the /test route blocks the event loop in this case.
function someHeavyWork() {
// like calling pbkdf2 function
}
app.get('/test', (req, res) => {
someHeavyWork();
res.json(data);
});
But that doesn't happen in this case.
function fakeDBCall() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(data);
}, 5000)
})
}
app.get('/test', async (req, res) => {
const data = await fakeDbCall();
res.json(data);
})
This may be because of my lack of understanding of how blocking works in the case of async/await.
Contrary to what it seems, await does not block. It's just syntactic sugar over promises. Nothing is blocked; it may look blocking to allow code to be synchronous, but that's just sugar over promises. For example, this may look synchronous:
const response = await fetch(…);
const json = await response.json();
const foo = JSON.parse(json); // Using json here, even though my request was async!
But it's not. Once you desugar it, all you get are promises, which are nonblocking:
fetch(…)
.then(response => response.json())
.then(json => {
const foo = JSON.parse(json);
});
It would be absolutely catastrophic if await were blocking. JavaScript runtimes are generally single threaded. That means user interaction and other processes would cease whenever you made a request or some other async operation such as using the filesystem. On a related note, this is, along with dynamic imports, are the main argument against top level await
The async function returns a promise and you are passing in the request and response, I would change the
res.json(data)
to
return res.json(data)
When an async function returns a value the promise is resolved, if the function contains an error the promise is rejected to just for cleanliness returning the res.json(data) will resolve the function.
This question already has answers here:
Returning a value from callback function in Node.js [duplicate]
(4 answers)
How do I return the response from an asynchronous call?
(41 answers)
Closed 5 years ago.
I don't understand callbacks in nodejs.
I need to get a podcast number from the database and use it in my code
I get right now from console.log
[Function: index]
Is there any solution in node.js to get variable from a database and reuse it later in the code?
var index = function( callback ) {
var podcast = 0;
User.findOne({ sessionID: this.event.session.sessionId }, function(err, user) {
if (err ||!user){
}
else {
console.log(user);
podcast = user.podcast;
}
});
callback( podcast );
};
index();
var callback = function(data) {
return data;
}
var iUseMyAnywhere = callback;
It look like it is impossible what I want to do in Node.js
It's completely possible. Only, you need to use asynchronous API's, which makes it ankward.
Is there any solution in node.js to get variable from a database and reuse it later in the code?
Not exactly. When you connect to a database -or, by the way, do anything asynchronously, like fetching something over http or reading from disc- you can't assign that thing right over:
var myUserFromDb = User.find('john doe', function(err, res){...}); //this will fail
Because that function you're passing as the second parameter will execute sometime in the future. User.find() itself doesn't return the user.
So, sadly, you can't just get the user in the user var and pass it to another module -let's say a podcast module-.
However, let's say you have a 'user.js' module, with exposes a withUser method than aks the database for a user and then calls a provided function with the user, when the db call is resolved.
And let's say you have a 'podcast.js' file/module with a getPodcast method that needs a user.
getPodcast can't just ask 'user.js' a user. However, it can ask for a function that will run with the user passed as parameter:
User.js
function withUser(callback){
User.find({_id: 1}, (err, user)=> {
callback(user);
})
}
podcast.js
function getPodcast(){
withUser( function(user){
//now we really have the user inside podcast.js, and we can work with it.
//Sadly, that will surely involve more asynchronous api's, which is painful.
})
}
Now getPodcast will have access to the user inside its parameter callback.
Is there any easier method rather than callback?
Yes, you should read about promises. When using promises, things are -a little less- painful. A promise api would work as:
User.js
function getUser(id){
//let's say we return a promise that wraps the `User.find` database request
}
podcast.js
getUser(userId).then(user => getPodcast(user)).then(podcastResult => ...)
This don't see really better. However, when you are working with promise api's, you can then start using async/await.
podcast.js
async function getPodcast(userId){
const user = await User.getUser(uesrId);
const otherAsyncThing = await ...someAsyncApiCall;
doAnythingWithUser(user); //this line won't execute until user is resolved, even if there aren't callbacks involved :-D
}
A final, unasked word of advice: when working with node.js, be sure you understand how callback api's and async things really work before writing a ton of code. Otherwise, you'll get really coupled and brittled code, where objects get passed through mountains of callbacks and code is unreadable and undebuggable :-D
PS. Edited to follow question.
Think like this, you get to a restaurant, sit down and ask a waitress for a coffee. But in the meanwhile you are not frozen, you are moving, doing things, talking, so once your coffee is ready, the waitress will bring it to you and you will stop other things that you are doing and drink your coffee.
So, it would become something like this:
User.findOne({ sessionID: this.event.session.sessionId }).exec().then(data => {
console.log(data);
}).catch(err => {
console.log(err);
});
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
}
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 5 years ago.
I am not really a Promise Ninja and I understand that I'm doing something wrong. However I cannot find some particular/simular problem to what I am having.
The problem: I use the Dexie.js wrapper for IndexedDB which is asynchronous. I have a global database which leads to some other dexie databases.
function handleDatabases() {
var result = [];
db.jobs.orderBy('title').filter(function(job) {
return job.someBooleanCondition;
}).each(function(job, cursor) {
let jobDetails = new Dexie(job.correspondingDB);
jobDetails.version(1).stores({
details: 'key,value1,value2'
});
jobDetails.details.get(someKey).then(function(detail) {
result.push({job: job, detail: detail});
})
}).catch(function(error) {
console.log(error);
});
handleResult(result);
}
I have rewritten it for SO with a maybe strange form but the end goal is that i can use the array result to handle some update. However since it is asynchronous it is always empty until you inspect it in console where it is never empty. How can I rewrite this to be synchronous?
You cannot expect to return the result when that result only becomes available asynchronously.
So you must stick with promises all the way (returning them each time), and let your function also return a promise. The caller must use then (or await if supported) to allow (asynchronous) access to the result.
Instead of pushing the {job: job, detail: detail} to a results variable, return it. It will become the promised value for jobDetails.details.get(..).then(..). If you return also that, you'll have an array of promises, which can then be resolved with Promise.all
Avoid to create new Promises as that usually leads to the promise constructor antipattern.
Also avoid using a variable (like results) that is used in several callbacks without being passed as argument. Instead try to construct and return that array as a promised value, so it can be used in the next then callback.
Here is the suggested (untested) code:
function handleDatabases() {
db.jobs
.orderBy('title')
.filter(job => job.someBooleanCondition)
.toArray(jobs =>
jobs.map(job => {
let jobDetails = new Dexie(job.correspondingDB);
jobDetails.version(1).stores({
details: 'key,value1,value2'
});
return jobDetails.details.get(someKey)
.then(detail => ({job: job, detail: detail}))
}) // is returned
)
.then(result => Promise.all(result))
.then(handleResult)
.catch(error => console.log(error));
}