Awaiting promise with nested awaited object resolves weirdly - javascript

I am using axios (npm module) to send a request to my server. This routine is part of a separate Node.js process I start, which should automatically close after its work is done.
However, I seem to be misinterpreting something about the promise. Consider the function I use to log these requests:
async log(request) {
return await axios.post('example.com', {
screenshot: await page.screenshot({type: 'png', encoding: 'base64'}),
});
}
I push all these calls to an array, so I know when to close the process:
this.awaitPromises.push(this.log(request));
later on in the code I call:
await Promise.all(this.awaitPromises);
As you can see there is a nested await page.screenshot(...) inside the parameters object of the axios call which creates a screenshot of the browser (puppeteer). This log(request) code however gives me an error indicating that the browser has closed unexpectedly: Protocol error (Page.captureScreenshot): Target closed.. This means that the await Promise.all() has resolved to soon because after that I close the browser.
The code does work when I add an extra .then() to the log function like so:
async log(request) {
return await axios.post('example.com', {
screenshot: await page.screenshot({type: 'png', encoding: 'base64'}),
}).then(() => {
// Do nothing.
});
}
The code now works as intended and the browser is only closed after the log function has (completely) been completed.
Anyone knows what the difference is here and why the last example works? It must have something to do with the inner await page.screenshot right?

Related

Node.js catch statement return does not stop executing

I'm trying to create a discord bot with the help of node.js
In which I need to call a function that may return discord API errors I handle them like this
interaction.user.send("Hi")
.catch(() => {return interaction.reply("...");
console.log("shouldnt run if an error accured")
However whenever that API error accurse the return statement unlike normally does not stop the code execution.
How do I stop the console.log statement in this code from executing when the exception accurse ?
the js is asynchronous so it puts the request of API in execution queue(not wait for response from api) and continue its execution that's why your console statement is running even if the error occurs.
interaction.user.send("Hi")
.then(() => {
// do whatever you want when it succeed
})
.catch((error) => {
// handle error
});
you can also checkout async await for the same.
As #ParthPatel indicates, interation.user.send() is returning a Promise which may not be rejected immediately upon error. Statements such as your console.log() will run before an error has a chance to occur and be caught.
However, these days there is the async await syntax you can use, which may help you simplify your code a bit depending on what you're trying to do.
try {
await interaction.user.send("Hi");
console.log('Shouldn\'t run if error occurred.');
} catch(e) {
interaction.reply('...');
}
Note that you can only use await inside of an async function, declared like this:
async function doSomething() {
// Do something here...
}
You can find more information here: https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await

Do not wait for a promise to execute and resolve it immediately [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 1 year ago.
I am having a downstream API call which is basically returning a Promise object instead of resolving it and executing it on point.
This is how I am calling the downstream:
const response = testClient.getSession(sessionId);
When I console.log(response) it is prinitng as Promise { <pending> } instead of desired result which it shoudl print from the downstream.
Having known very little about Async/Await, Promises etc I would like to know whihc category does it fall in? And how do I execute that promise first and do the rest of the steps.
However, I have found a temporary work around like this:
response.then(function(result) {
console.log(result.body);
});
But ideally I would want to store the result into response object then and there itself. Please help me understand this. Thanks in advance
What you called a temporary workaround using then is actually the correct way of waiting on the Promise to be fulfilled. You cannot 'force' it to be fulfilled, because the code will execute as fast as it can and allow you to carry on working until then (that's the point of promises) - but you can wait on it to be fulfilled:
You ought also to check to see if the promise gets 'rejected' using catch - ie produces an error:
response.then( function(result) {
console.log(result.body);
}).catch( function(error) {
console.log(error);
});
To use await:
async function thingy(){
const response = await testClient.getSession(sessionId);
}
To use await and check for errors:
async function thingy(){
try {
const response = await testClient.getSession(sessionId);
} catch (error) {
console.error(error);
}
}
Note that when using async, you do generally need to be in a function declared as async.
The idea of the Promise is to allow your program to carry on doing things whilst you wait for the promise to be fulfilled/rejected - for example, you might make several requests at once, and wait for them all:
Promise.all([
getStuff('one'),
getStuff('two'),
getStuff('n')
]);

Module-scope variable refuses to be overwritten by an async function. Why?

The module-scope variable "output" refuses to be overwritten by the async function "retrieveTextWrapper", and I cannot figure out why. My objective is to output the text on StackOverFlow's homepage. retrieveTextWrapper successfully scrapes this information, but I can't seem to assign this content to the output variable. What am I doing wrong? How can I print the scraped information from the main() function?
Note: I am using electron version 3.0.4 because bypassing CORS is less of a pain on that version.
const {BrowserWindow, app} = require('electron')
output = "this should be overwritten by the retrieveTextWrapper method"
async function main(){
navigate();
win.openDevTools();
await win.webContents.once('dom-ready',retrieveTextWrapper);
console.log(output);
//prints "this should be overwritten by the retrieveTextWrapper method"
}
function navigate() {
win = new BrowserWindow({width:900,height:900});
win.loadURL(`https://stackoverflow.com/`);
}
function retrieveText(){
return `document.querySelector("*").innerText`;
}
async function retrieveTextWrapper(){
output = await win.webContents.executeJavaScript(retrieveText().replace("*", "#content"));
}
app.on('ready',main)
win.webContents.once() does not return a promise (since interfaces generally don't accept both callbacks and return a promise at the same time).
Therefore await doesn't wait for the asynchronous operation to complete. Therefore, you're looking at output before its value has been reassigned. await only does something useful when you await a promise that is connected to the asynchronous operation you're trying to wait for.
To confirm this timing issue, add a unique console.log() statement before and after the await win.webContents.once('dom-ready',retrieveTextWrapper); and inside of retrieveTextWrapper and then you can see the sequencing of these log messages.
Yep, everything changes as it should within retrieveTextWrapper function. And your explanation makes a lot of sense. However, is it possible to wait for the callback to finish (using some other syntax aside from await)? That way, I can use the updated value for other operations in the main function?
You have a couple options.
You could "promisify" win.webContents.once() so you could then use await with it.
You could put the callback inline and put the rest of your code in main inside that callback (a classic way of dealing with asynchronous operations).
Here's an example of promisifying win.webContents.once():
function waitForDomReady() {
return new Promise((resolve, reject) => {
// may want to check if document already has dom-ready and resolve immediately
win.webContents.once('dom-ready', resolve);
});
}
And, you could then use it like this:
async function main(){
navigate();
win.openDevTools();
await waitForDomReady();
await retrieveTextWrapper();
console.log(output);
}
This assumes that the code in retrieveTextWrapper that calls win.webContents.executeJavaScript() does actually return a promise when it's done. If not, you have to promisify that too.

Running an asynchronous function inside another with async/await fails to connect/fetch resource

I'm building a web application using Node and Express. I have an HTML page which is making requests to my API using the Fetch API. In my table, I have an X button for each row, that deletes that task (it's working fine.) However, the deletion function deleteTask then calls my tableTasks function to reload the table with the new data. The request fails, with the error messages:
Failed to load resource: Could not connect to server
Unhandled Promise Rejection: TypeError: Could not connect to the server.
With a link to the correct API endpoint which, when opened from a different source (browser, Postman, different button on form page) yields the results correctly.
I have already tried relocating the await keyword to await tableTasks from inside deleteTask, and used return. Now, my code runs inside tableTasks, and it stops on this line:
const response = await fetch("http://XX.XX.XXX.XXX:3000/list");
With the above errors. I have tried changing the await to .then syntax and placing the remainder of tableTasks inside a callback function, but to no avail.
In deleteTask:
tableTasks(false);
In tableTasks:
async function tableTasks() {
//Initialization
const response = await fetch("http://XX.XX.XXX.XXX:3000/list");
const data = response.json();
//Do stuff with data
}
My expectation is that when this function is called, it reloads the table, but it errors out. Do I need to place an extra await somewhere, or wait on the response in a different manner?
Always encapsulate your async function in a try/catch block like so:
async function f() {
try{
await asynchronous_method();
}catch(err){
//Handle Rejection.
}
}
the fetch command returns a promise. A promise has 2 parts. Resolve and Reject. The await keyword returns whatever is returned from the resolve part. If there is an error in the fetch the reject will execute. You need to handle that also else you will get the above error. Also seems there is some server connection error.

Does async/await blocks event loop? [duplicate]

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.

Categories