Why promise.resolve wait forever in this case? - node.js

I am a nodejs's newbie. I'm testing a promise and have an issue.
Here's my issue.
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
app.use(bodyParser.json());
app.get('/', async function(req,res){
console.log(222);
res.send("Hello!!!");
let check;
try {
console.log(3333);
check = await test();
console.log(4444);
} catch (err) {
console.log("Error : ", err);
}
console.log(111, check);
});
function test () {
return new Promise (resolve => {
console.log(5555);
app.get('/test', function(req, res) {
count = count + 1;
res.send(count.toString());
resolve("hahahaha");
})
})
}
app.listen(9000, function(){
console.log("hehehehe");
});
In a get 'http://localhost:9000' callback, I wait for 'http://localhost:9000/test' result to do something. The thing is, it's working fine for the first time. But since the 2nd, the promise.resolve() function doesn't work.
Here is my first time log:
hehehehe
222
3333
5555
4444
111 'hahahaha'
And here is my second time log :
222
3333
5555
the promise.resovle() doesn't work. It's waiting forever and I don't understand.
EDIT : Here is the solution for using EventEmitter to do signup and smsVerifyCode after modified by the help of Mr. #Aritra Chakraborty
var express = require('express');
var userControler = require('../Controler/user');
var router = express.Router();
var utils = require('../Helper/Utils');
var user_model = require('../Models/user');
const TIMEOUT_VERIFY = 300000;
const CODE_EXPIRED = 0;
const CODE_VALID = 1;
const CODE_INVALID = 2;
const CONTACT_EXISTED = 3;
const DATABASE_ABUSED = 4;
const events = require('events');
const emitter = new events.EventEmitter();
function timeout_verify_sms_emitter (time) {
setTimeout(() => {
emitter.emit('timeout_sms');
}, time);
}
function verify_code(codeGen) {
return new Promise((resolve, reject)=>{
emitter.on("verifySMS", (data)=>{
if (data === codeGen) {
resolve(CODE_VALID);
}
else {
resolve(CODE_INVALID);
}
})
emitter.on('timeout_sms', () =>{
resolve(CODE_EXPIRED);
});
})
}
router.get('/',function(req,res){
res.send("Welcome to the Earth!!!");
})
router.post('/signup', async function(req,res){
let verifyCode;
let checkContact;
let codeGen = utils.generateCode();
try {
checkContact = await user_model.checkContact(userData.contact);
if (checkContact === true) {
res.send(CONTACT_EXISTED);
}
else {
//call send sms to contact function here
//exam : sendSMS(contact)
//
timeout_verify_sms_emitter(TIMEOUT_VERIFY);
verifyCode = await verify_code(codeGen);
}
}
catch (err) {
console.log("Error : ", err);
}
if (verifyCode === CODE_EXPIRED) {
res.send(CODE_EXPIRED);
}
else if (verifyCode === CODE_VALID) {
var result = userControler.processUserData(req.body);
if (result) {
res.send(CODE_VALID);
}
else {
res.send(DATABASE_ABUSED);
}
}
else {
res.send (CODE_INVALID);
}
})
router.post('/signup/verifySMS', function(req, res){
emitter.emit("verifySMS", req.body.smsCode);
})
module.exports = router;

According to the above code:
Whenever you do a get request to / you are CREATING a /test path.
So, the first time it works because /test path has one handler.
Second or more time it doesn't works because,
The /test route has multiple handler now.
How express works is, routes get executed according to their declaration time. (Think middlewares)
Now, Second time the /test will have 2 handlers. And when you hit /test the first handler runs. And as this is not a middlware it doesn't go to the next handler. Now, the first handler has a different resolve function then the second one. So the second resolve function doesn't run at all.(Think closure)
For what are you trying to do most web implementations use long polling. Because if you wait too long for the second route it will throw a timeout error. Means you periodically hit an api to get the status of something. Means you create 3 routes.
A main route, like /signup
A second route like /sendsms
A third route where you send a particual identifier and it returns some positve/negative value. like /sendsms/check, where you might pass the phone number.
Otherwise if you don't care about timeouts you can use EventEmitter inside the /test route.
EDIT1:
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
app.use(bodyParser.json());
const events = require('events');
const emitter = new events.EventEmitter();
let count = 0;
app.get('/', async function (req, res) {
console.log(222);
res.send("Hello!!!");
let check;
try {
console.log(3333);
check = await test();
console.log(4444);
}
catch (err) {
console.log("Error : ", err);
}
console.log(111, check);
});
app.get('/test', function (req, res) {
count = count + 1;
emitter.emit("test",count.toString());
res.send(count.toString());
})
function test(){
return new Promise((res)=>{
emitter.on("test", (data)=>{
res(data);
})
})
}
app.listen(9000, function () {
console.log("hehehehe");
});
EDIT2:
Regarding the solution, you need to handle the timeout differently. Let's say your timeout is 3sec. And the SMS route took 100sec to get a response or maybe it didn't even got a response. Then your function will be stuck there.
function test(sendSMSTime) {
return new Promise((res, rej) => {
emitter.on("test", (data) => {
.
.
res(data);
})
emitter.on('timeout', rej);//On timeout it will run.
})
}
function timeoutEmitter(timeout) {
setTimeout(() => {
emitter.emit('timeout');
}, timeout)
}
app.post('/signup', async function (req, res) {
try {
timeoutEmitter(3000);
.
.
}catch{
}
});

Related

API logic to get latest weather information

I'm writing weather API which fetches METAR and TAFS data from aviation weather stations.
Data changes once an hour.
Need to fetch latest weather information, but in my code I cannot make it to fetch it after an hour.
var express = require('express');
var router = express.Router();
const ADDS = require('adds');
var moment = require('moment');
// adds moment to the locals
router.use((req, res, next)=>{
res.locals.moment = moment;
next();
});
const avWet = Promise.all([
ADDS('metars', {
stationString: 'KCRX,KMSL,KSNH,KTUP',
hoursBeforeNow: 1,
mostRecentForEachStation: true
}),
ADDS('tafs', {
stationString: 'KTUP,KMSL',
hoursBeforeNow: 1,
mostRecentForEachStation: true
})
])
.then(results => {
const parsedData = results;
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('wet_av', {
parsedData
})
})
});
module.exports = router;
You don't have to restart Your app to get data, so You don't have to use nodemon or another watcher which will restart the app too.
Simply write Your method which will get data and store it in variable
Schedule it to check for new data
Call that method to get data and push to render
const express = require('express');
const router = express.Router();
const ADDS = require('adds');
const moment = require('moment');
// adds moment to the locals
router.use((req, res, next)=>{
res.locals.moment = moment;
next();
});
let weatherInformation = null;
let weatherInformationParsedAt = 0;
async function getWeatherInformation() { // [1] method that will get data from api
const startOfHour = moment().startOf('hour').format('YYYY-MM-DD hh:')+'00';
const currentTimeString = moment().startOf('minute').format('YYYY-MM-DD hh:mm');
// if there is no data then need to parse
let needToParse = !weatherInformation;
// forcing to parse if it's 00'th minute of hour
if (startOfHour === currentTimeString &&
Date.now() - weatherInformationParsedAt > 30000
) { // if start of hour
needToParse = true;
}
// forcing to parse if data is old more than 2 mins, making sure we have fresh data
if (!needToParse &&
weatherInformation &&
Date.now() - weatherInformationParsedAt > 120000
) {
needToParse = true;
}
try {
// if need to parse so let's fetch again (that's for caching)
if (needToParse) {
const query = {
hoursBeforeNow: 1,
mostRecentForEachStation: true,
};
const results = await Promise.all([
ADDS('metars', {
...query,
stationString: 'KCRX,KMSL,KSNH,KTUP',
}),
ADDS('tafs', {
...query,
stationString: 'KTUP,KMSL',
}),
]);
weatherInformation = results;
weatherInformationParsedAt = Date.now();
}
}
catch (error) {
console.error(error);
}
return weatherInformation;
}
setInterval(getWeatherInformation, 1000); // [2] every second call method which checks for changes
router.get('/', async (req, res, next) => {
const parsedData = await getWeatherInformation(); // [3] calling method to get weather information
res.render('wet_av', {
parsedData,
});
});
module.exports = router;

node.js Why data from post requests that is saved in a json file resets to initial empty array after restart server?

I am working with express node.js, and I am trying to save datas from post request in a json file. but for some reason when I restart the server, my data from post request was supposed to save in roomDB.json file doesnt remain instead it resets to initial empty array...
Could anyone please advice? thank you very much.
here is my code
//saving function
const fs = require("fs");
exports.save =(data, PATH) =>{
return new Promise((resolve, reject) => {
fs.writeFile(PATH, JSON.stringify(data), function(err) {
if (err) {
reject(err);
} else {
resolve();
}
});
});
}
// code in router file to make requests
const express = require("express");
const router = express.Router();
const fs = require("fs");
const rooms = ("./roomDB.json");
const { addRoom} = require("./rooms");
router.get("/", (req, res)=>{
fs.readFile("roomDB.json", (err, data)=>{
if(err) return res.status(400);
res.send(roomDB_PATH)
})
});
router.get("/:id", (req, res)=>{
res.send("connect to a room");
});
router.post("/", (req, res)=>{
let roomName = req.body;
if(!roomName.name){
res.status(404);
res.end();
return;
}
let room =addRoom(roomName.name);
res.status(201).send(room)
})
module.exports = router;
*/
const uuid = require("uuid");
let roomdatas;
const {save} = require("./save");
const roomDB_PATH = "roomDB.json";
try {
roomdatas = JSON.parse(fs.readFileSync(roomDB_PATH));
} catch (e) {
roomdatas = []
save(roomdatas, roomDB_PATH);
}
const addRoom = (roomName) => {
roomName = roomName.trim().toLowerCase();
const existingRoom = roomdatas.find((room) => room.name === roomName);
if (existingRoom) {
return { error: 'chatroom has existed' };
}
let room = {
name: roomName,
id: uuid.v4(),
messages: [],
users: [],
created: +new Date()
};
roomdatas.push(room);
save(roomdatas, roomDB_PATH);
return { room };
};
module.exports ={addRoom};
I'm assuming that you are encountering an error with the JSON.parse(fs.readFileSync(roomDB_PATH)); call. This code runs every time your server is started (when you import the file into your router file), and if it encounters an error it is resetting the file to an empty array. Try logging the error to see what is causing it. You're currently completely suppressing the error with no way to tell why it is failing.

asynchronous code not working with event listener

*Edited for clarity and to reflect changes:
Hello. I'm struggling to understand why my async functions are working just fine individually, and when chained together, but not when chained together and fired off in an event listener....
worth noting: when i try to run this without an even listener, data passes from my app, through my post route, and to my endpoint the way it should, it is then returned to my app through the correct route, etc. However, i do get an error in the console that says :
error SyntaxError: Unexpected token < in JSON at position 0
however, when i try to run my chain of async functions on the click of a dom element i don't get the above error, data is passed to my post route, i can log the object that is posted:
Object: null prototype] { zipcode: '97206', feel: 'confused' }
but then my server doesn't save any data, and my get route is never triggered, and nothing gets sent back to my app.......
i'm fairly lost.....
full server and app code below:
server:
const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cors());
app.use(express.static('public'));
//test values
let projectData = {
date: "today",
temp: "38",
feelings: "confused"
};
app.get("/", (req, res) => {
});
app.post("/postData", (req, res) => {
console.log(req.body)
projectData = {
date: req.body.date,
temp: req.body.temp,
feelings: req.body.feelings
}
console.log("post route was hit, saved:", projectData);
res.redirect("/")
});
app.get("/getData", (req, res) => {
console.log("getData route was hit, sent: ", projectData)
res.send(projectData);
})
app.listen(3000, (req, res) => {
console.log("Listening on port 3000");
});
app
let newDate = getDate();
const apiKey = "5fd7b3a253e67a551e88ff34a92b9e02";
const baseURL = "http://api.openweathermap.org/data/2.5/weather?";
// zip=${}&APPID=${}&units=metric;
const userData = {};
// returns the date //
function getDate() {
let d = new Date();
let newDate = d.getMonth() + "." + d.getDate() + "." + d.getFullYear();
return newDate;
}
//constructs string for api call, adds temp to userData object
const getData = async (apiUrl, zip, key) => {
const url = `${apiUrl}zip=${zip}&APPID=${key}&units=metric`;
const result = await fetch(url);
try {
let newData = await result.json()
// console.log(newData.main.temp)
userData.temp = newData.main.temp
}catch(error) {
console.log("error", error);
}
}
// getData(baseURL, 97206, apiKey)
// .then(result => {
// console.log(userData)
// })
//saves contents of userData Object to server
const postData = async (url, data) => {
const result = await fetch(url, {
method: "POST",
credentials: "same-origin",
headers: {
"Content-type": "application/json"
},
body: JSON.stringify(data)
});
try {
const newData = await result.json();
// console.log(newData);
return newData;
} catch (error) {
console.log("error", error);
}
};
// postData('/postData', userData);
//updates interface with projectData values from server
const updateUI = async url => {
const result = await fetch(url);
try {
const newData = await result.json();
document.getElementById("date").innerHTML = newData.date;
document.getElementById("temp").innerHTML = newData.temp;
document.getElementById("feelings").innerHTML = newData.feelings;
} catch (error) {
console.log("error", error);
}
};
// updateUI("/getData")
// THIS WORKS
userData.date = newDate;
userData.feelings = document.getElementById("feel").value;
const zipCode = document.getElementById("zipcode").value;
getData(baseURL, 97206, apiKey).then(result => {
postData("/postData", userData).then(result => {
updateUI("/getData");
});
});
// THIS DOESNT WORK
// document.getElementById("btn").addEventListener("click", e => {
// userData.date = newDate;
// userData.feelings = document.getElementById("feel").value;
// const zipCode = document.getElementById("zipcode").value;
// getData(baseURL, zipCode, apiKey).then(result => {
// postData("/postData", userData).then(result => {
// updateUI("/getData");
// });
// });
// });
EDIT:
I realized that the information that was being passed through the post route when my async functions are fired off by an event listener was actually just the form input element values, rather than the contents of the fetch/post request. after i removed the name attributes from the form inputs, i'm getting no data at all hitting my post route......yet the corosponding function works fine with not in the event listener.
I'm stumped.
well, the syntax error was solved by using .text() instead of .json() on the results of my post request.
adding e.preventDefault() as the first line of my event listener callback fixed the event listener, and now everything is working as it should.

console.log not fired when promise is executed

I have a js file in which i export a promise and i call it in another file.
I dnt understand why the console logs are not fired when i call the promise,
maybe i dnt fully understand how promises work or maybe sth is wrong with the way i call the promise
pub.js
var config = require('../config');
var q = 'tasks';
var open = require('amqplib').connect('amqp://'+ config.rabbitmq.url);
module.exports = open.then(function(conn) {
return conn.createChannel();
}).then(function(ch) {
return ch.assertQueue(q).then(function(ok) {
console.log('inside publisher')
const r = ch.sendToQueue(q, Buffer.from('something to do'));
console.log('r',r)
return r
});
}).catch(console.warn);
index.js (where i call the promise)
var express = require('express');
var router = express.Router();
var publisher = require('../connectors/pub');
var rabbitPromise = () => (
new Promise((resolve,reject)=>{
publisher
.then(res=>{
console.log('-----------------------')
console.log('publishing now',res)
resolve(res)
})
})
);
/* GET home page. */
router.get('/', async(req, res, next) => {
const result_pub = await rabbitPromise()
res.send('ok')
});
I expected to see the console.log('inside publisher') but i don't see it i only get console.log('-----------------------') and console.log('publishing now',res),
the chaining in pub.js is not right. In order to call the promise, it would be something more like this :
module.exports = open
.then(function(conn) {
return conn.createChannel();
})
.then(function(ch) {
return ch.assertQueue(q);
})
.then(function(ok) {
console.log('inside publisher')
const r = ch.sendToQueue(q, Buffer.from('something to do'));
console.log('r',r)
return r
})
.catch(console.warn);

node js async writting from variable which is randomly updating like wifi networks

I am trying to display the wifi networks around me on the web page.
But due to the asynchronous nature of node js. I am getting the value undefined as it is returning the value before the scanning is completed.
I am using
1. node 'wireless-tools' package
2. http server module which came by default in node js.
here is my code:
function scan() {
iwlist.scan({
iface : 'wlp1s0',
show_hidden : true
},
function(err, networks) {
return networks;
}
});
}
const http = require('http');
const hport = 8080;
const haddr = "localhost";
const Server = http.createServer(function(req, res){
var n = scan();
res.write(n);
res.end();
});
Server.listen(hport, haddr, () => {
console.log('Server running at ' + haddr + ' ' + hport);
});
It is displaying me undefined on the page.
Please use async/await to work with it.I had not tested the code.Please check the code.
function scan() {
return new Promise((resolve, reject) => {
iwlist.scan({
iface: 'wlp1s0',
show_hidden: true
},
function (err, networks) {
if (err) {
return reject(err);
}
return resolve(networks);
}
);
});
}
const http = require('http');
const hport = 8080;
const haddr = "localhost";
const Server = http.createServer(async (req, res) => {
try {
var n = await scan();
res.write(n);
res.end();
}
catch (error) {
res.write(error);
}
});
You are affecting the immediate return value of iwlist.scan() into the n variable. This function uses a classic nodejs callback to handle the asynchronous behavior, so it might always return undefined, as the result will go into the callback.
Two solutions:
1) Affect your variable result inside the callback
function scan(myCallback) {
iwlist.scan({
iface : 'wlp1s0',
show_hidden : true
}, myCallback);
});
const Server = http.createServer(function(req, res){
var n;
scan(function(error, result) {
n = result;
res.write(n);
res.end();
});
});
2) You transform the existing function into a promise using Promisify
var scanAsync = utils.promisify(iwlist.scan)
function async scan() {
// Will return the result as a promise
return scanAsync({
iface : 'wlp1s0',
show_hidden : true
});
};
try {
var n = await scan();
} catch (e) {
// Handle error
}

Resources