I am in the process of creating a data handler and as part of that I need to download a file to then parse and work with as part of the handler.
I have wrapped the request for the file in a promise and asynchronously called the method from other methods
I have included the handler from a separate test file as follows
let DataHandler = require('./handler');
const handler = new DataHandler();
console.log(handler.getSupervisors());
The getSupervisors method is as follows
async getSupervisors() {
const parsed = await this.loadFile();
let supervisorList = [];
parsed.controllers.map(controller => {
if (controller.rating === 11 || controller.rating === 12){
supervisorList.push(controller);
}
});
return supervisorList;
}
This calls the loadFile method which simply calls the update method and before returning the json file required. The next 2 methods are update and downloadFile which I think are my 2 issues, they are as follows
async update() {
let body = await this.downloadFile();
const parsedJSON = JSON.parse(body);
parsedJSON.updated_date = new Date();
const json = JSON.stringify(parsedJSON);
fs.writeFile('vatsimData.json', json, function(err, result) {
if(err) console.log(err);
});
}
downloadFile() {
return new Promise((resolve, reject) => {
const urlList = [
'http://us.data.vatsim.net/vatsim-data.json',
'http://eu.data.vatsim.net/vatsim-data.json',
'http://apac.data.vatsim.net/vatsim-data.json'
];
const url = urlList[Math.floor(Math.random()*urlList.length)];
request(url, (error, response, body) => {
if (error) reject(error);
if (response.statusCode !== 200) {
reject('Invalid status code <' + response.statusCode + '>');
}
resolve(body);
});
});
}
The output of the getSupervisors method should be an array of all the 'supervisors' within the json file, instead it returns Promise { <pending> } and this error
(node:3160) UnhandledPromiseRejectionWarning: SyntaxError: Unexpected end of JSON input
at JSON.parse (<anonymous>)
at DataHandler.loadFile (D:\Projects\VATSIM-Data-Handler\handler.js:57:15)
at process._tickCallback (internal/process/next_tick.js:68:7)
(node:3160) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:3160) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
I have tried putting the whole of the update method within a .then callback after calling downloadFile but it did not work
My loadFile method is as follows
async loadFile(){
await this.update();
return(JSON.parse(fs.readFileSync('vatsimData.json', {encoding:'utf-8'})));
}
The reason you are getting a promise is because, you are not awaiting the async function when you console.log(handler.getSupervisors());
you should do
handler.getSupervisors.then(val => console.log(val));
The error may be because the body response from the server may be malformed. try console.log body before calling JSON.parse() and use try catch to wrap and prevent unhandled errors
Update
From comments, you are trying to parse a file read with incorrect encoding
console.log(JSON.parse(fs.readFileSync('vatsimData.json',{encoding:'utf-8'})));
Add the encoding value in options.
Update
async update() {
let body = await this.downloadFile();
const parsedJSON = JSON.parse(body);
parsedJSON.updated_date = new Date();
const json = JSON.stringify(parsedJSON);
fs.writeFileSync('vatsimData.json', json, function(err, result) {
if(err) console.log(err);
}); // convert it to sync or resolve with a new promise when you complete the write to wait for the loadFile to have the file ready
}
Related
The problem is that i am getting UNhandledPromiseRejection error eveen though i think i have handled all the cases. The code flows from profileRoutes to Controller to Utils where the error comes first.
Inside the profileRoutes.js
router.get('/:username', async (r, s) => {
try{
let profileData = await getProfileData(r.params.username);
s.json({ success: true, payload: profileData });
}catch(err){
console.log('ending request processing by responding a error');
s.status(500).json({ success: false, message: 'err[0].message' });
}
});
Inside the controllers/index.js
const fetchQueue = [getUserRepos];
async function getProfileData(username) {
let profileData = {};
try{
let results = await Promise.all(fetchQueue.map(item => item(username)));
for (let i = 0; i < results.length; i++) {
profileData[getKeys[i]] = results[i];
}
return profileData;
}catch(err){
console.log('error log in controller/index getProfileData function');
throw err;
}
}
const getUserRepos = async (username) => {
try {
// const res = await utils.gqlSender(username, 'userRepos', { createdAt });
const res = await utils.gqlSender(username, 'userReposData');
return res.user.repositories;
} catch (err) {
console.log('error log in controller/index getUserRepos function');
throw err;
}
};
Inside the utils/index.js
const gqlSender = async (username, type, opt = {}) => {
axios.post('', {
query: gqlQuery(username, type, opt) // generates a needed graphQL query
}).then(res => {
if(res.data.errors) { // this is where the error is recieved and so i reject promise.
console.log('bef###re');
return Promise.reject (res.data.errors);
}
console.log('###',res.data);
return res.data;
}).catch(err => {
console.log('error in making axios request inside utils/index gqlSender function');
throw err;
// return Promise.reject(err);
});
The stack trace on making get request to /:username is-
error log in controller/index getUserRepos function
error log in controller/index getProfileData function
ending request processing by responding a error
bef###re
error in making axios request inside utils/index gqlSender function
(node:11260) UnhandledPromiseRejectionWarning: [object Array]
(node:11260) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
(node:11260) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
I dont think i am missing any Promise Rejection.
Any help is appreciated. Thanks.
i have referred these answers previously -
What's the difference between returning value or Promise.resolve from then()
Do I need to return after early resolve/reject?
Your gqlSender function is not returning the promise that will get rejected, so it is not handled anywhere. You should write either
const gqlSender = (username, type, opt = {}) => {
return axios.post('', {
// ^^^^^^
query: gqlQuery(username, type, opt) // generates a needed graphQL query
}).then(res => {
if (res.data.errors) {
console.log('error in making axios request inside utils/index gqlSender function');
throw res.data.errors;
} else {
console.log('###',res.data);
return res.data;
}
});
};
or
const gqlSender = async (username, type, opt = {}) => {
// ^^^^^
const res = await axios.post('', {
query: gqlQuery(username, type, opt) // generates a needed graphQL query
});
if (res.data.errors) {
console.log('error in making axios request inside utils/index gqlSender function');
throw res.data.errors;
} else {
console.log('###',res.data);
return res.data;
}
}
In this code, file is being opened and read successfully.
var objFs = require('fs')
async function openFile() {
await objFs.open('new.js', 'r', (argError, argFD) => {
if (argError)
throw -1
else
console.log("File opened!")
})
// This object is the promise.
const objReadResult = await objFs.readFile('new.js', (argError, argData) => {
if (argError)
throw 2
else
console.log('File read!')
})
await objFs.writeFile('new.js', (argError, objReadResult) => {
try
{
if( argError )
throw argError
else
console.log("File written")
}
catch( arg )
{
console.log(arg)
}
})
}
openFile()
What is the way to extract the data from await of readFile and pass it to writeFile?
The way I have written the writeFile function is producing the following error:
(node:21737) UnhandledPromiseRejectionWarning: TypeError [ERR_INVALID_CALLBACK]: Callback must be a function. Received undefined
at maybeCallback (fs.js:145:9)
at Object.writeFile (fs.js:1332:14)
at openFile (/home/sulakshana/Documents/nodejs/programs/async_await.js:29:14)
(node:21737) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:21737) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
File opened!
File read!
Few issues I can see:
there is a mix of syntaxes - async/await while also providing callbacks to the fs calls;
no need to call fs.open before fs.readFile;
there are no data to be written in fs.writeFile [docs];
there should be await-ing at the end;
and the final (the actual error reported) - there should be try...catch block to catch and react to any errors that occur.
Example implementation:
const fs = require('fs').promises;
async function openFile() {
const objReadResult = await fs.readFile('new.js');
console.log('File read!');
await fs.writeFile('new.js', /* PROVIDE DATA TO WRITE */);
console.log('File written');
}
(async () => {
try {
await openFile();
console.log('Program is done');
} catch (err) {
console.error(err);
}
})();
I have a few methods that use the request module to get images from URLs and returns it as a Promise, it works but when the image isn't found it rejects the promise with the status code 404. I looked into that warning and it is said that to handle the rejection you have to setup a catch callback after then() but I don't use then(), I use async/await.
This is the code to get the picture:
/**
* Returns picture from S3
* #param {String} filename Name of the file with extension
* #returns {String} Base64 string of the file
*/
getPictureFromS3: function (filename) {
return new Promise((resolve, reject) => {
let url = this.getURLFromS3(filename);
request(url, (err, res, body) => {
if (err) reject(err);
if (res.statusCode !== 200) {
reject(`Invalid status code <${res.statusCode}>`);
}
resolve(new Buffer.from(body).toString('base64'));
});
});
}
And this is how I call the method:
try{
socket.on('server get pictures', () => db.getPictures(data=>{
if(data!==null){
data.forEach(async e=>{
let picture = await utils.getPictureFromS3(e.getFilename());
});
}
}));
}catch(err){
console.log(err);
}
Full warning:
(node:256) UnhandledPromiseRejectionWarning: Invalid status code <404>
(node:256) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which
was not handled with .catch(). (rejection id: 1)
(node:256) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Using Node v10.14.2.
There are two problems here
You care trying to do async operation inside forEach which doesn't work with promises. You need to use for..of loop or Promise.all if you can do things in parallel.
The exception is raise inside the listener so it will not bubble up outside so your catch wouldn't be executed. You need to move your try catch inside. Something like this
socket.on("server get pictures", () =>
db.getPictures(data => {
if (data !== null) {
for(const e of data) {
try {
let picture = await utils.getPictureFromS3(e.getFilename());
} catch (err) {
console.log(err);
}
}
}
})
);
I am using puppeteer to build a small app to automatically fill forms online. I am using Gmail as an example to test the functionality without clicking the submit button.
I open the Gmail URL and locate the username input field by using a function called findInputFieldByName. The code works fine, webpage opens and I can see puppeteer printing test#gmail.com in the username field.
The code is:
const puppeteer = require('puppeteer');
const gmailUrl='https://accounts.google.com/signin/v2/identifier?continue=https%3A%2F%2Fmail.google.com%2Fmail%2F&service=mail&sacu=1&rip=1&flowName=GlifWebSignIn&flowEntry=ServiceLogin';
const userName='test#gmail.com';
const findInputFieldByName = (page,inputName) => {
return page.$('input[name="'+inputName+'"]');
}
(async () => {
try {
const Browser = await puppeteer.launch({headless:false,
defaultViewport:{width:600,height:800}
});
const page=await Browser.newPage();
page.on('load', () => console.log('Page loaded ' + page.url()));
await page.goto(gmailUrl);
const userNameElement = await findInputFieldByName(page,'identifier').catch(error => {
console.log('The following error occurred: ' + error);
});
await userNameElement.type(userName,{delay:50});
} catch(e) {console.log('main program error:' + e);
}
})();
As you can see the code is looking for an input field whose name is identifier in the returned HTML. However, if I were to use a different name such as identifier1, which is not on the Gmail Login page, I want the code to throw a custom error message saying "Input field name identifier1 not found" and the code should stop.
If I change
const userNameElement = await findInputFieldByName(page,'identifier').catch(error => {
console.log('The following error occurred: ' + error);
});
to
const userNameElement = await findInputFieldByName(page,'identifier1').catch(error => {
console.log('The following error occurred: ' + error);
});
I get the following error:
main program error:TypeError: Cannot read property 'type' of null
I understand that this is because the function findInputFieldByName has not failed. It just returned a null object because there is no input field with the name identifier1. The code actually failed when it tried to type the email address.
However, my question is how can I throw an error from within the findInputFieldByName function so that it stops the at the const userNameElement = await findInputFieldByName.. execution?
I have tried to use promises within the findInputFiledByName function as:
const findInputFieldByName = (page,inputName) => {
let promise=new Promise(function (resolve,reject){
var elm= page.$('input[name="'+inputName+'"]');
if (elm.length>0) {
resolve(elm);
} else {
reject(Error('Could not find input name ' + inputName));
}
});
}
but I am getting the following message:
main program error:TypeError: Cannot read property 'catch' of undefined
(node:12332) UnhandledPromiseRejectionWarning: Error: Could not find input name identifier1
at C:\Users\test1.js:11:10
at new Promise (< anonymous >)
at findInputFieldByName (C:\Users\test1.js:6:14)
at C:\Users\test1.js:26:32
at < anonymous >
at process._tickCallback (internal/process/next_tick.js:188:7) (node:12332) UnhandledPromiseRejectionWarning: Unhandled promise
rejection. This error originated either by throwing inside of an async
function without a catch block, or by rejecting a promise which was
not handled with .catch(). (rejection id: 1) (node:12332) [DEP0018]
DeprecationWarning: Unhandled promise rejections are deprecated. In
the future, promise rejections that are not handled will terminate the
Node.js process with a non-zero exit code.
How should I handle the custom errors? I am new to Javascript to any help is much appreciated.enter code here
First of all, return the promise instead of assigning it to a variable.
const findInputFieldByName = (page, inputName) => {
return new Promise(((resolve, reject) => { // <-- Return it
After that, you can throw error if need.
await findInputFieldByName(page,'identifier').catch(error => {
throw ('The following error occurred: ' + error) // <-- pass the error to main program
});
Working with Node and mssql to pull queries for five different databases to de-dupe against each other and merge into a more unified schema.
My process follows this algorithm:
create a shared pool by calling this function:
const getPoolConnection = async () => {
try {
let pool = await mssql.connect(`mssql://${username}:${password}#${server}/`);
return pool;
} catch (err) {
console.error(err);
}
};
This function creates the pool and returns it to the calling function. username, password, and server are imported and scoped to this file.
Then we query each database and assign the result to a property on an object. This is accomplished via a forEach loop:
lists.forEach(list => {
fullData[list] = db.queryDatabase(pool, customers[list].query).catch(err => console.error(err));
})
which calls this function:
const queryDatabase = async (pool, query) => {
try {
let result = await pool.request().query(query);
// console.log(result);
return result.recordset, pool;
} catch (err) {
console.error(err);
}
};
now in order to keep post-processing from occuring before all database calls return data, I've wrapped the entire set of calls in a Promise.all() call in the main index.js file. This is the calling funciton:
const { customers } = require('./query');
const util = require('./util');
const db = require('./db');
fullData = {};
(async () => {
let pool = await db.getPoolConnection();
let lists = Object.keys(customers);
Promise.all(
lists.forEach(list => {
fullData[list] = db.queryDatabase(pool, customers[list].query).catch(err => console.error(err));
})
)
.then(results, pool => {
console.dir(results);
db.closePoolConnection(pool);
})
.catch(err => console.error(err));
})();
What I don't understand is this error that occurs when attempting to debug the application:
(node:18908) UnhandledPromiseRejectionWarning: TypeError: Cannot read
property 'Symbol(Symbol.iterator)' of undefined warning.js:18
at Function.all ()
at c:\Users\rutherfordc\Documents\GitHub\migration-plus\index.js:10:11
at
at process._tickCallback (internal/process/next_tick.js:188:7) (node:18908) UnhandledPromiseRejectionWarning: Unhandled promise
rejection. This error originated either by throwing inside of an async
function without a catch block, or by rejecting a promise which was
not handled with .catch(). (rejection id: 1) warning.js:18
(node:18908) [DEP0018] DeprecationWarning: Unhandled promise
rejections are deprecated. In the future, promise rejections that are
not handled will terminate the Node.js process with a non-zero exit
code. warning.js:18 (node:18908) UnhandledPromiseRejectionWarning:
ReferenceError: results is not defined warning.js:18
at c:\Users\rutherfordc\Documents\GitHub\migration-plus\index.js:15:11
at
at process._tickCallback (internal/process/next_tick.js:188:7) (node:18908) UnhandledPromiseRejectionWarning: Unhandled promise
rejection. This error originated either by throwing inside of an async
function without a catch block, or by rejecting a promise which was
not handled with .catch(). (rejection id: 2)
Promise.all() needs an array. So you could do something like this:
fullData = [];
(async () => {
let pool = await db.getPoolConnection();
let lists = Object.keys(customers);
lists.forEach(list => {
fullData.push(db.queryDatabase(pool, customers[list].query).catch(err => console.error(err));
}));
Promise.all(fullData)
.then((results, pool) => {
console.dir(results);
db.closePoolConnection(pool);
})
.catch(err => console.error(err));
})();
Update:
Also as J. Pichardo suggested in his answer, in case of multiple parameters, the parameters should be enclosed in parentheses.
Documentation
The error is non-logical but syntactical, the following lines in the main function
.then(results, pool => {
console.dir(results);
db.closePoolConnection(pool);
})
The arguments of the arrow function should be surrounded by parentheses, like:
.then((results, pool) => {
console.dir(results);
db.closePoolConnection(pool);
})
ReferenceError: results is not defined
So, the then is looking for a results variable and since it is an async function when it crashes it will be a UnhandledPromiseRejection.
Update:
As the other answer says Promise.all receives either multiple promises or an array of promises, so you could do something like:
var fullData = lists.map(list => db.queryDatabase(pool, customers[list].query).catch(err => console.error(err)));
Promise.all(fullData).then(...)