Function does not wait until Promise is resolved - node.js

I'm developing a simple discord bot and I am attempting to print some general data about a certain player. I recently learned about async/await and I attempted to implement it into my code. It does not seem to work however because when I first trigger this code, it prints null but on subsequent triggers it will print the correct data, indicating that my function did not wait for the Promise to resolve.
async function stats(){
data = await NBA.stats.playerInfo({ PlayerID: curry.playerId });
}
stats();
data = JSON.stringify(data);
console.log(data);
The variable data is a global variable declared at the top of my program and initially initialized to null.

If I understand your intention correctly, what you want is to asynchronously fetch some data into data and then display it on the console.
Your implementation of stats is doing the fetch correctly. But your problem is, the part where you log to console has no dependence on the fetch being completed.
When you call a function that has been declared async, you are saying that you want it to execute "in the background" so to speak. The interpreter won't wait for that function to finish executing.
stats(); // begin executing this function
data = JSON.stringify(data); // but don't wait for it to finish before moving on to this line
console.log(data);
Clearly, that's not what you want. Instead, you want to wait for stats to finish what it's doing before logging data. stats being an async function returns a Promise, so you can do this:
function logData() {
console.log(JSON.stringify(data));
}
stats().then(logData);

Related

Why can't I deep copping a DICT out of a function in Node.js?

I'm using request to call an API which gives me movies data in an object called body, but when I try to pass it to an array and console log it, the terminal shows me an empty array.
let copy = [];
const request = require('request');
request('https://www.omdbapi.com/?t=JOKER&apikey=b04f2804', { json: true }, (err, res, body) => {
if (err) { return console.log(err);}
copy.push(JSON.parse(JSON.stringify(body)))
});
console.log(copy);
First off, the request() library is deprecated and it is not recommended that you write new code with it. There is a list of alternatives (that all support promises) here.
Then second, the request() library is non-blocking and asynchronous. That means that when you call request(), it starts the operation and then immediately returns. The code after the call to request() then continues to execute BEFORE request() calls its callback. So, you're trying to examine the array before its value has been set. This is a classic issue in asynchronous programming. There are a number of ways to handle this. If you stayed with the request() library, then you must either use the result INSIDE the callback itself or you can call some other function from within that callback and pass it the array.
With one of the listed alternatives that all support promises (my favorite is the got() library), you can then use await which is often a preferred programming style for asynchronous operations:
const got = require('got');
async function someFunction() {
let result = await got('https://www.omdbapi.com/?t=JOKER&apikey=b04f2804').json();
// you can use the result here
console.log(result);
// Or you can return it and it will become the resolved value
// of the promise this async function returns
return result;
}
someFunction().then(result => {
// can use result here
}).catch(err => {
console.log(err);
});
Note that when you are writing asynchronously retrieved data to a higher scoped variable as you are when you do copy.push(), that is typically a warning that you're doing something wrong. This is because NONE of the code in the higher scope will know when the data is available in that variable. There are rare cases when you might do that (like for implementing a cache), but these have to be situations where the data is being stored only for future reference, not for immediate use. 99.9% of the time when we see that construct, it's a wrong way to program with asynchronously retrieved data.

Using 'body' returned from Request

I am using https://github.com/request/request.
The example that is given is:
const request = require('request');
request('http://www.google.com', function (error, response, body) {
console.error('error:', error); // Print the error if one occurred
console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received
console.log('body:', body); // Print the HTML for the Google homepage.
});
How can I use body elsewhere in my code? I want something like return body but nothing works. I can't use it anywhere!
You cannot directly return an asynchronous result from outside the request() callback. This is not unique to this particular function, but is how all asynchronous callbacks work in Javascript.
When you call request() it starts an asynchronous operation and turns it over to native code. The JS interpreter then goes about it's merry way executing the rest of your Javscript after this function call (not in the callback, but after that). So, you should be able to immediately see that the body result does not exist yet, but the rest of your Javascript is already executing.
Then, some indeterminate time later (depending upon how responsive the server is that you're contacting and how big the result is), when the JS interpreter has nothing else to do, the callback gets called and the body result is available.
So, the ONLY place in your code that you know the body result is good and is available is INSIDE that callback. So, the general way of programming with an asynchronous operation like this in Javascript is that you use the result inside the callback. Any code that needs to use that result gets put inside that callback or you can put the code in a separate function and call that function from inside the callback and pass the body result as an argument to the function.
If you wanted to wrap this request in a function and communicate back that result to the caller (which is not exactly what you show here, but another way to write the code), I'd suggest you first read How do I return the response from an asynchronous call? because it outlines the various ways you can communicate back the body result from your asynchronous operation (another callback or use promises).
To many newbie Javascript developers this seems somewhat heretical. What do you mean I can't just call a function, get the result and return it from the function? Well, that's the extra complication with Javascripts non-blocking, asynchronous I/O model that is entirely event driven. Once you get down the learning curve, you will find a huge number of advantages in the language that flow from this architectural model, but you will have to deal with this extra complication.
The language is evolving to make this type of programming simpler with the use of promises in async functions and then the use of await to "wait" on a promise. If you use the request-promise library instead of the request library (request-promise is derived from request), then you can deal with a promise as the return value and you have more options.
const rp = require('request-promise');
async getMeSomeData() {
try {
let body = await rp('http://www.google.com');
console.log(body);
// can put code here that uses body
return body; // becomes the resolved value of the returned promise
} catch(err) {
console.log(err);
throw err; // makes sure the returned promise is rejected
}
});
getMeSomeData().then(body => {
// use the body here
}).catch(err => {
// error here
});
Note: I showed possibly using the body value or err inside getMeSomeData() or at the caller. Usually, you would do one or the other, but I wanted to show both ways.

How to stop class/functions from continuing to execute code in Node.js

I have made a few questions about this already, but maybe this question would result in better answers(i'm bad at questions)
I have one class, called FOO, where I call an async Start function, that starts the process that the class FOO was made to do. This FOO class does a lot of different calculations, as well as posting/getting the calculations using the node.js "requets" module.
-I'm using electron UI's (by pressing buttons, that executes a function etc..) to create and Start the FOO class-
class FOO {
async Start(){
console.log("Start")
await this.GetCalculations();
await this.PostResults()
}
async PostResults(){
//REQUESTS STUFF
const response = {statusCode: 200} //Request including this.Cal
console.log(response)
//Send with IPC
//ipc.send("status", response.statusCode)
}
async GetCalculations(){
for(var i = 0; i < 10; i++){
await this.GetCalculation()
}
console.log(this.Cal)
}
async GetCalculation(){
//REQUEST STUFF
const response = {body: "This is a calculation"} //Since request module cant be used in here.
if(!this.Cal) this.Cal = [];
this.Cal.push(response)
}
}
var F1 = new FOO();
F1.Start();
Now imagine this code but with A LOT more steps and more requests ect. where it might take seconds/minutes to finish all tasks in the class FOO.
-Electron got a stop button that the user can hit when he wants the calculations to stop-
How would I go about stopping the entire class from continuing?
In some cases, the user might stop and start right after, so I have been trying to figure out a way to STOP the code from running entirely, but where the user would still be able to create a new class and start that, without the other class running in the background.
I have been thinking about "tiny-worker" module, but on the creation of the worker, it takes 1-2 seconds, and this decreases the purpose of a fast calculation program.
Hopefully, this question is better than the other ones.
Update:
Applying the logic behind the different answers I came up with this:
await Promise.race([this.cancelDeferred, new Promise( async (res, req) => {
var options ={
uri: "http://httpstat.us/200?sleep=5000"
}
const response = await request(options);
console.log(response.statusCode)
})])
But even when the
this.cancelDeferred.reject(new Error("User Stop"));
Is called, the response from the request "statuscode" still gets printed out when the request is finished.
The answares I got, shows some good logic, that I didn't know about, but the problem is that they all only stop the request, the code hanlding the request response will still execute, and in some cases trigger a new request. This means that I have to spam the Stop function until it fully stops it.
Framing the problem as a whole bunch of function calls that make serialized asynchronous operations and you want the user to be able to hit a Cancel/Stop button and cause the chain of asynchronous operations to abort (e.g. stop doing any more and bail on getting whatever eventual result it was trying to get).
There are several schemes I can think of.
1. Each operation checks some state property. You make these operations all part of some object that has a aborted state property. The code for every single asynchronous operation must check that state property after it completes. The Cancel/Stop button can be hooked up to set this state variable. When the current asynchronous operation finishes, it will abort the rest of the operation. If you are using promises for sequencing your operations (which it appears you are), then you can reject the current promise causing the whole chain to abort.
2. Create some async wrapper function that incorporates the cancel state for you automatically. If all your actual asynchronous operations are of some small group of operations (such as all using the request module), then you can create a wrapper function around whichever request operations you use that when any operation completes, it checks the state variable for you or merges it into the returned promise and if it has been stopped, it rejects the returned promise which causes the whole promise chain to abort. This has the advantage that you only have to do the if checks in one place and the rest of your code just switches to using your wrapped version of the request function instead of the regular one.
3. Put all the async steps/logic into another process that you can kill. This seems (to me) like using a sledge hammer for a small problem, but you could launch a child_process (which can also be a node.js program) to do your multi-step async operations and when the user presses stop/cancel, then you just kill the child process. Your code that is monitoring the child_process and waiting for a result will either get a final result or an indication that it was stopped. You probably want to use an actual process here rather than worker threads so you get a full and complete abort and so all memory and other resources used by that process gets properly reclaimed.
Please note that none of these solutions use any sort of infinite loop or polling loop.
For example, suppose your actual asynchronous operation was using the request() module.
You could define a high scoped promise that gets rejected if the user clicks the cancel/stop button:
function Deferred() {
let p = this.promise = new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
});
this.then = this.promise.then.bind(p);
this.catch = this.promise.catch.bind(p);
this.finally = this.promise.finally.bind(p);
}
// higher scoped variable that persists
let cancelDeferred = new Deferred();
// function that gets called when stop button is hit
function stop() {
// reject the current deferred which will cause
// existing operations to cancel
cancelDeferred.reject(new Error("User Stop"));
// put a new deferred in place for future operations
cancelDeferred = new Deferred();
}
const rp = require('request-promise');
// wrapper around request-promise
function rpWrap(options) {
return Promise.race([cancelDeferred, rp(options)]);
}
Then, you just call rpWrap() everywhere instead of calling rp() and it will automatically reject if the stop button is hit. You need to then code your asynchronous logic so that if any reject, it will abort (which is generally the default and automatic behavior for promises anywa).
Asynchronous functions do not run code in a separate thread, they just encapsulate an asynchronous control flow in syntactic sugar and return an object that represents its completion state (i.e. pending / resolved / rejected).
The reason for making this distinction is that once you start the control flow by calling the async function, it must continue until completion, or until the first uncaught error.
If you want to be able to cancel it, you must declare a status flag and check it at all or some sequence points, i.e. before an await expression, and return early (or throw) if the flag is set. There are three ways to do this.
You can provide a cancel() function to the caller which will be able set the status.
You can accept an isCancelled() function from the caller which will return the status, or conditionally throw based on the status.
You can accept a function that returns a Promise which will throw when cancellation is requested, then at each of your sequence points, change await yourAsyncFunction(); to await Promise.race([cancellationPromise, yourAsyncFunction()]);
Below is an example of the last approach.
async function delay (ms, cancellationPromise) {
return Promise.race([
cancellationPromise,
new Promise(resolve => {
setTimeout(resolve, ms);
})
]);
}
function cancellation () {
const token = {};
token.promise = new Promise((_, reject) => {
token.cancel = () => reject(new Error('cancelled'));
});
return token;
}
const myCancellation = cancellation();
delay(500, myCancellation.promise).then(() => {
console.log('finished');
}).catch(error => {
console.log(error.message);
});
setTimeout(myCancellation.cancel, Math.random() * 1000);

Effect of the position of a callback in an asynchronous function

i'm trying to understand asynchronous callbacks in NodeJS by following this guide and i have a question about the position of callback() in the code snippet below.
var fs = require('fs')
var myNumber = undefined
function addOne(callback) {
fs.readFile('number.txt', function doneReading(err, fileContents) {
myNumber = parseInt(fileContents)
myNumber++
callback()
})
}
function logMyNumber() {
console.log(myNumber)
}
addOne(logMyNumber)
Here, my file 'number.txt' contains the number 1 and the output of this entire code snippet is 2. This appears to be invoking callback() after the file is read, and the output is expected. However, moving callback() outside of fs.readFile() but inside addOne() as shown below has confused me as the output is now undefined.
var fs = require('fs')
var myNumber = undefined
function addOne(callback) {
fs.readFile('number.txt', function doneReading(err, fileContents) {
myNumber = parseInt(fileContents)
myNumber++
})
callback()
}
function logMyNumber() {
console.log(myNumber)
}
addOne(logMyNumber)
Does this mean that in the second example, callback() is invoked before fs.readFile() has completed?
Your thinking is right. Node.js executes fs.readFile but DOES NOT wait for it's completion.
So the execution moves to next statement, which invokes the callback and the result is undefined because the previous command has not yet finished.
Asynchronous programming is very interesting, but easy thing.
Asynchronous executions means that program won't run your code synchronously line by line waiting for completing each line of code WHEN YOU ARE EXECUTING BLOCKING CODE, but instead it will do concurrent execution (concurrency).
Blocking code is when you are doing for example http requests, reading files as in your example or DOM events in browser. Basically blocking code is code which is not depended on your code. Basically blocking code is when your program should wait unknown duration till their completion to continue.
On basic javascript operations like arithmetic and array operations, loops e.g.
Code runs synchronously because they are not depended on external resources.
That is why we have callbacks in JavaScript to not wait for their completion.
Callbacks helps us to run code after asynchronous code executed.
Asynchronous means that our code is NON-BLOCKING.
So in your code when you run
fs.readFile('number.txt', function doneReading(err, fileContents) {
myNumber = parseInt(fileContents)
myNumber++
})
callback()
It will start running fs.readFile and immediately will start execution of callback function.
But when when you run
fs.readFile('number.txt', function doneReading(err, fileContents) {
myNumber = parseInt(fileContents)
myNumber++
callback()
})
It will start executing fs.readFile and within his callback doneReading function will do operations with myNumber and will execute callback
Most of JavaScript asynchronous functions have callback functions.
You can read about blocking and non-blocking codes here, also here about callbacks
Also you can read about promises and async/await.
They are very cool things, that helps you to struct your code as you would in synchronous environment, but runs asynchronous code.

Is the following node.js code blocking or non-blocking?

I have the node.js code running on a server and would like to know if it is blocking or not. It is kind of similar to this:
function addUserIfNoneExists(name, callback) {
userAccounts.findOne({name:name}, function(err, obj) {
if (obj) {
callback('user exists');
} else {
// Add the user 'name' to DB and run the callback when done.
// This is non-blocking to here.
user = addUser(name, callback)
// Do something heavy, doesn't matter when this completes.
// Is this part blocking?
doSomeHeavyWork(user);
}
});
};
Once addUser completes the doSomeHeavyWork function is run and eventually places something back into the database. It does not matter how long this function takes, but it should not block other events on the server.
With that, is it possible to test if node.js code ends up blocking or not?
Generally, if it reaches out to another service, like a database or a webservice, then it is non-blocking and you'll need to have some sort of callback. However, any function will block until something (even if nothing) is returned...
If the doSomeHeavyWork function is non-blocking, then it's likely that whatever library you're using will allow for some sort of callback. So you could write the function to accept a callback like so:
var doSomHeavyWork = function(user, callback) {
callTheNonBlockingStuff(function(error, whatever) { // Whatever that is it likely takes a callback which returns an error (in case something bad happened) and possible a "whatever" which is what you're looking to get or something.
if (error) {
console.log('There was an error!!!!');
console.log(error);
callback(error, null); //Call callback with error
}
callback(null, whatever); //Call callback with object you're hoping to get back.
});
return; //This line will most likely run before the callback gets called which makes it a non-blocking (asynchronous) function. Which is why you need the callback.
};
You should avoid in any part of your Node.js code synchronous blocks which don't call system or I/O operations and which computation takes long time (in computer meaning), e.g iterating over big arrays. Instead move this type of code to the separate worker or divide it to smaller synchronous pieces using process.nextTick(). You can find explanation for process.nextTick() here but read all comments too.

Resources