Node Script wont stop after async await in a loop - javascript

const axios = require("axios");
const Parser = require("./utils/parser");
const WebStore = require("./models/webstore");
const mongoose = require("mongoose");
require("./db/connection");
require("dotenv").config();
const url = "";
const app_secret = process.env.APP_SECRET;
const buff = new Buffer(app_secret);
const base64 = buff.toString("base64");
axios
.get(url, {
headers: {
Accept: "application/json",
Authorization: `Basic ${base64}`,
},
params: {
from_date: "2020-02-19",
to_date: "2021-02-21",
},
})
.then(async (response) => {
const data = response.data.split("\n");
const events = [];
data.forEach((point) => {
try {
const dat = Parser.toDB(JSON.parse(Parser.parser(point)));
events.push(dat);
} catch (err) {}
});
events.forEach(async (event) => {
try {
const e = new WebStore(event);
await e.save();
console.log("saved");
} catch (err) {
console.log("fail");
}
});
})
.catch((err) => {
console.error(err);
});
So After the execution the script outputs saved multiple times but the script is not closed itself.
The data is stored in the database after the execution I have tried mongoose.disconnect() and I've also tried mongoose.connection.close(). How do I resolve this issue ?

Related

Can't get the proper object back

Hi i'm using MeaningCloud's api to get the proper object back once it analyses a string of text or a url for the Natural language processing (NLP). But it doesn't return the proper object.
Right now the code returns a string with the text "[Object object]" on the HTML page. I need it to return the results of the api call which returns the proper JSON object(that I can see in the console) in a proper "key/value" pair format on the HTML page.
Here's my script:
const baseURL = "https://api.meaningcloud.com/sentiment-2.1";
const key = "Your_api_key";
const submitBtn = document.getElementById("submitBtn");
submitBtn.addEventListener("click", (e) => {
e.preventDefault();
const url = document.getElementById("url").value;
if (url !== "") {
getData(baseURL, url, key)
.then(function (data) {
postData("/add", { data: data });
}).then(function () {
receiveData()
}).catch(function (error) {
console.log(error);
alert("Invalid input");
})
}
})
const getData = async (baseURL, url, key) => {
const res = await fetch(`${baseURL}?key=${key}&txt=${url}`)
try {
const data = await res.json();
return data;
}
catch (error) {
console.log("error", error);
}
}
const postData = async (url = "", data = {}) => {
const response = await fetch(url, {
method: "POST",
credentials: "same-origin",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
data: data
})
});
try {
const newData = await response.json();
return newData;
} catch (error) {
console.log(error);
}
};
const receiveData = async () => {
const request = await fetch('/all');
try {
// Transform into JSON
const allData = await request.json()
console.log(allData)
// Write updated data to DOM elements
document.getElementById('result').innerHTML = allData;
}
catch (error) {
console.log("error", error);
// appropriately handle the error
}
}
I have another main file that's the server.js file which I run using node server.js that renders the html page properly but the script doesn't render the results on the page properly. You can signup on meaningcloud for a free api key that has a very convenient number of calls you can make.

Javascript for loop variable is strange when writing on a file

I have this code in './utils/url.js'. it basically makes the application/x-www-form-urlencoded content form:
const ContentForm = ()=>{
let params = new URLSearchParams()
const randomString = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
params.append('email', `${randomString}#gmail.com`)
return params;
}
module.exports = ContentForm;
The email parameter is a random string.
and index.js:
const axios = require('axios').default;
const fs = require('fs');
const params = require('./utils/url')
for (let i = 0; i < 1000; i++) {
const config = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}
// sending post with data of web/application the url http://somewhere.com/my-account/
axios.post('http://somewhere.com/my-account/',params(),config, {
})
.then(function (response) {
console.log("request successfully made")
})
.catch(function (error) {
// seeing the error response code
console.log(error.response.status);
})
.finally(function () {
// always executed
fs.writeFileSync('./r.txt',String(i));
})
}
So I want that the 'i' variable be written in the ./r.txt. It actually means that which request we are sending write now. but the problem is that it is really strange in it:
look the video of r.txt changes here
You are running 1000 asynchronous operations in a loop. You start them sequentially, but they all run in parallel. Then as each one finishes each one calls fs.writeFileSync() and it's a race to see which one calls it when. It will be random in what order each one finishes, which is what I think your video shows.
You can sequence them to be in order using await like this:
const axios = require("axios").default;
const fs = require("fs");
const params = require("./utils/url");
async function run() {
for (let i = 0; i < 1000; i++) {
const config = {
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
};
// sending post with data of web/appliaction the url http://somewhere.com/my-account/
await axios
.post("http://somewhere.com/my-account/", params(), config, {})
.then(function(response) {
console.log("request succesfully made");
})
.catch(function(error) {
// seeing the error response code
console.log(error.response.status);
})
.finally(function() {
// always executed
fs.writeFileSync("./r.txt", String(i));
});
}
}
run().then(() => {
console.log("done");
}).catch(err => {
console.log(err);
});
Or, reorganized a bit to not mix await and .then() in the same function like this:
const axios = require("axios").default;
const fs = require("fs");
const params = require("./utils/url");
async function run() {
for (let i = 0; i < 1000; i++) {
const config = {
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
};
// sending post with data of web/appliaction the url http://somewhere.com/my-account/
try {
let response = await axios.post("http://somewhere.com/my-account/", params(), config, {});
console.log("request succesfully made");
} catch(error) {
console.log(error.response.status, error);
} finally {
fs.writeFileSync("./r.txt", String(i));
}
}
}
run().then(() => {
console.log("done");
}).catch(err => {
console.log(err);
});
async function writeToFile(){
for (let i = 0; i < 1000; i++) {
const config = {
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
};
// sending post with data of web/appliaction the url http://somewhere.com/my-account/
await axios
.post("http://somewhere.com/my-account/", params(), config, {})
.then(function (response) {
console.log("request succesfully made");
})
.catch(function (error) {
// seeing the error response code
console.log(error.response.status);
})
.finally(function () {
// always executed
fs.writeFileSync("./r.txt", String(i));
});
}
}

Chaining async functions doesn't work correctly

I'm trying to chain two async functions but it seems that the second one is being executed before the first one.
Here is my code
function performAction(e) {
const ZIP = document.getElementById('zip').value;
const fellings = document.getElementById('feelings').value;
console.log(`${baseURL}${ZIP},us&appid=${key}`);
getWeather(baseURL, ZIP, key,).then((data) => {
postData('/addweather', {temperature: data.main.temp ,date:newDate, userResponse: fellings })
}).then(
updateUI()
)}
and This is getWeather()
const getWeather = async(baseURL, ZIP, key) => {
let URL = `${baseURL}${ZIP}&appid=${key}`;
const res = await fetch(URL)
try{
const data = await res.json();
return data;
}catch(error){
console.log("error", error);
}}
and this is postData() which is supposed to execute after the getWeather function is excuted but it isn't.
const postData = async ( url = '', data = {}) => {
console.log(`This is what we fetch ${data.temperature}`);
console.log(`This is what we fetch ${data.date}`);
console.log(`This is what we fetch ${data.userResponse}`);
const response = await fetch(url, {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
},
// Body data type must match "Content-Type" header
body: JSON.stringify(data),
});
try {
const newData = await response.json();
console.log(`This is the new Data ${newData.temperature}`);
return newData;
}catch(error){
console.log("error", error);
}}
and this is updateUI()
const updateUI = async () => {
const request = await fetch('/getweather');
try{
const allData = await request.json();
console.log('Get request');
document.getElementById('date').innerHTML = allData.date;
document.getElementById('temp').innerHTML = allData.temperature;
document.getElementById('content').innerHTML = allData.userResponse;
}catch(error){
console.log("error", error);
}}
What happens is that the UI gets updated first so it gets the value of undefined for the first time, and when I reload the page and enter new data the UI get's updated with the data from last time.
You need to return the promise returned from postData:
getWeather(baseURL, ZIP, key,).then((data) => {
return postData('/addweather', {temperature: data.main.temp ,date:newDate, userResponse: fellings })
}).then(() => {
return updateUI()
})
Another way you could write this is like this:
async function run() {
await getWeather(baseURL, ZIP, key)
await postData('/addweather', {temperature: data.main.temp ,date:newDate, userResponse: fellings })
await updateUI()
}
your postData() is also an async function. Therefore you have to await this aswell:
getWeather(baseURL, ZIP, key,).then(async (data) => {
await postData('/addweather', {temperature: data.main.temp ,date:newDate, userResponse: fellings })
}).then(
updateUI()
)}
I didn't have done javascript in a while, but i guess it is more clear in this way:
const performAction = async (e) => {
const ZIP = document.getElementById('zip').value;
const fellings = document.getElementById('feelings').value;
console.log(`${baseURL}${ZIP},us&appid=${key}`);
try{
const data = await getWeather(baseURL, ZIP, key,);
const postData= await postData('/addweather', {temperature: data.main.temp ,date:newDate, userResponse: fellings });
} catch(e) {
console.log(e)
} finally {
updateUI();
}
Also you dont have to await the parsing of the json and the try catch should contain your request:
const postData = async ( url = '', data = {}) => {
console.log(`This is what we fetch ${data.temperature}`);
console.log(`This is what we fetch ${data.date}`);
console.log(`This is what we fetch ${data.userResponse}`);
try {
const response = await fetch(url, {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
},
// Body data type must match "Content-Type" header
body: JSON.stringify(data),
});
const newData = response.json();
console.log(`This is the new Data ${newData.temperature}`);
return newData;
}catch(error){
console.log("error", error);
}}

Cannot get value from local storage inside function without converting to async function

I have made a wrapper for fetch function for my API calls in react-native. I dont want to pass JWT token everytime that I make an API call, so I thought that fetching it inside wrapper will fix it for me, but I cannot get it to work because of async nature...
useFetch.js
// import qs from "querystring";
import { getUserAuthToken } from "../storage";
const responseChecker = async (response) => {
let error = "";
let data = {};
let statusCode = null;
if (!response.ok) {
error = "Something went wrong";
statusCode = response.status;
} else {
statusCode = response.status;
data = await response.json();
}
return { statusCode, error, data };
};
const fetchAuthToken = getUserAuthToken();
const useFetch = (baseURL, authHeader = null) => {
console.log(fetchAuthToken);
**//Cannot get this token in time for the API call ^**
const defaultHeader = {
Accept: "application/json",
"Content-Type": "application/x-www-form-urlencoded",
key: "1c419c7e-3a34-49f0-9192-b48d4534dff3",
Authorization: authHeader ? authHeader :fetchAuthToken,
};
const customFetch = (
url,
method = "GET",
body = false,
headers = defaultHeader,
) => {
const options = {
method,
headers,
credentials: "include",
};
if (body) options.body = body;
return fetch(url, options);
};
const get = async (endpoint) => {
const url = `${baseURL}${endpoint}`;
const response = await customFetch(url, "GET");
return responseChecker(response);
};
const post = async (endpoint, body = {}) => {
const url = `${baseURL}${endpoint}`;
const response = await customFetch(url, "POST", body);
return responseChecker(response);
};
const put = async (endpoint, body = {}) => {
const url = `${baseURL}${endpoint}`;
const response = await customFetch(url, "PUT", body);
return responseChecker(response);
};
return {
get,
post,
put,
};
};
export default useFetch;
storage.js
import AsyncStorage from "#react-native-community/async-storage";
export const getUserAuthToken = async () => {
try {
const userToken = await AsyncStorage.getItem("userAuthToken");
return userToken;
} catch (e) {
console.log("error");
}
};
exportAPI.js
import useFetch from "./fetch";
const LOCAL_IP = "192.168.0.131";
export const authAPI = (header) => useFetch(`http://${LOCAL_IP}:8000`, header);
export const activityAPI = useFetch(`http://${LOCAL_IP}:8000`);
Steps.js
import React, { useEffect, useState } from "react";
import { Text, Platform } from "react-native";
import { CardXLarge } from "../../../components/Cards/";
import fitnessKitApis from "../../../utilities/fitnessKits";
import { activityAPI } from "../../../utilities/apis";
const StepCard = (props) => {
const fetchStepsFromFitnessKits = async () => {
if (Platform.OS === "android") {
await fitnessKitApis.historicSteps().then((res) => {
setSteps(res);
});
} else {
await fitnessKitApis.historicSteps((result) => setSteps(result));
}
};
const [steps, setSteps] = useState(0);
useEffect(() => {
fetchStepsFromFitnessKits();
const requestParams = { date: new Date(), steps };
const { data, statusCode, error } = activityAPI.get(
"/v1/user/steps/",
requestParams,
);
// console.log(data, statusCode, error);
}, [steps]);
return (
<CardXLarge>
<Text>{steps}</Text>
</CardXLarge>
);
};
export default StepCard;
I know I can pass authHeader from component but that will result in adding 2-3 lines of code in every component which is not super convenient.
Thanks
If you don't want to use async/await in a function to get items from asyncStorage. You can use either callback or promise in place of async/await.
Callback:
AsyncStorage.getItem('data1', (error, data1) => {
// perform your logic here.
});
Promise:
AsyncStorage.getItem('data1').then(data1=>{
// perform your logic here.
}).catch(error=>{
// handle error
})
I have personally used callback for getItem and worked perfectly. I have not tested the promise version but I expect to do the same work as callback.
...
const fetchAuthToken = getUserAuthToken();
...
Your getUserAuthToken is an asynchronous function and here it is not being awaited. To guarantee that asynchronous call is finished you have to await it or use callbacks as #HungrySoul suggested.
You can't await something outside of an asynchronous function.
Solution that I would suggest is creating a class UseFetch and passing the arguments through the constructor. The argument being here the JWT token that you are getting from the AsyncStorage.
Also, another thing that can be done and is a good practice - use redux for managing the state and keeping the JWT token. You might look into that. It will take a bit longer but it will make your code more elegant.
Edit: Or, you might try something like this.
Keep in mind that you have to wait for promises to resolve before you use what was promised.
Here we are using a closure. You will have to pass an argument (which is an async function) to the useFetchBuilder. That function will be awaited and provide the JWT. You can use the getUserAuthToken for that purpose.
Keep in mind that you have to use await or wait for the promise to resolve before using this function. Problem might be somewhere else in your code - maybe the life cycle methods.
I hope this helped.
const responseChecker = async (response) => {
let error = "";
let data = {};
let statusCode = null;
if (!response.ok) {
error = "Something went wrong";
statusCode = response.status;
} else {
statusCode = response.status;
data = await response.json();
}
return { statusCode, error, data };
};
const useFetchBuilder = async (userTokenProvider) => {
const userToken = await userTokenProvider();
return (baseURL, authHeader = null) => {
const defaultHeader = {
Accept: "application/json",
"Content-Type": "application/x-www-form-urlencoded",
key: "1c419c7e-3a34-49f0-9192-b48d4534dff3",
Authorization: authHeader ? authHeader : userToken,
};
const customFetch = (
url,
method = "GET",
body = false,
headers = defaultHeader,
) => {
const options = {
method,
headers,
credentials: "include",
};
if (body) options.body = body;
return fetch(url, options);
};
const get = async (endpoint) => {
const url = `${baseURL}${endpoint}`;
const response = await customFetch(url, "GET");
return responseChecker(response);
};
const post = async (endpoint, body = {}) => {
const url = `${baseURL}${endpoint}`;
const response = await customFetch(url, "POST", body);
return responseChecker(response);
};
const put = async (endpoint, body = {}) => {
const url = `${baseURL}${endpoint}`;
const response = await customFetch(url, "PUT", body);
return responseChecker(response);
};
return {
get,
post,
put,
};
};
}
export default useFetchBuilder;
I moved getUserAuthToken function down to each request method function in useFetch function where I can await for the response. Then it all worked perfectly.. Also I could have use getUserAuthToken but usingAsyncStorage.getItem seems much cleaner
modified fetch.js
// import qs from "querystring";
import AsyncStorage from "#react-native-community/async-storage";
const responseChecker = async (response) => {
let error = "";
let data = {};
let statusCode = null;
if (!response.ok) {
error = "Something went wrong";
statusCode = response.status;
} else {
statusCode = response.status;
data = await response.json();
}
return { statusCode, error, data };
};
const useFetch = (baseURL, authHeader = null) => {
const defaultHeader = {
Accept: "application/json",
// "Content-Type": "application/x-www-form-urlencoded",
"Content-Type": "application/json",
key: "1c419c7e-3a34-49f0-9192-b48d4534dff3",
Authorization: authHeader,
};
const customFetch = (
url,
method = "GET",
body = false,
headers = defaultHeader,
) => {
const options = {
method,
headers,
credentials: "include",
};
if (body) options.body = JSON.stringify(body);
return fetch(url, options);
};
const get = async (endpoint) => {
await AsyncStorage.getItem("userAuthToken").then((result) => {
defaultHeader.Authorization = result;
});
const url = `${baseURL}${endpoint}`;
const response = await customFetch(url, "GET");
return responseChecker(response);
};
const post = async (endpoint, body = {}) => {
await AsyncStorage.getItem("userAuthToken").then((result) => {
defaultHeader.Authorization = result;
});
const url = `${baseURL}${endpoint}`;
const response = await customFetch(url, "POST", body);
return responseChecker(response);
};
const put = async (endpoint, body = {}) => {
await AsyncStorage.getItem("userAuthToken").then((result) => {
defaultHeader.Authorization = result;
});
const url = `${baseURL}${endpoint}`;
const response = await customFetch(url, "PUT", body);
return responseChecker(response);
};
return {
get,
post,
put,
};
};
export default useFetch;

Only one of many Firebase Functions being invoked

I have 2 Firebase functions that I want to execute when there is an Http request, one function (createEmailList) to save data in the Firebase database, the other (zohoCrmHook) to to save in a 3rd party CRM called Zoho.
When the functions are deployed to Firebase, the functions log shows that both are properly deployed. However, when the Http request is made from the frontend, the log shows that only one of the functions (createEmailList) is being executed.
As the log shows, the first function createEmailList is being executed and the data shows up in the Firebase database with no problem. However, The second function zohoCrmHook is not even being executed.
index.js
const functions = require('firebase-functions');
const admin = require("firebase-admin")
const serviceAccount = require("./service_account.json");
const createEmailList = require('./createEmailList')
// zoho
const zohoCrmHook = require('./zohoCrmHook')
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://landing-page.firebaseio.com"
})
exports.zohoCrmHook = functions.https.onRequest(zohoCrmHook)
exports.createEmailList = functions.https.onRequest(createEmailList)
createEmailList.js
const admin = require('firebase-admin')
const cors = require('cors')({ origin: true })
module.exports = (req, res) => {
cors(req, res, () => {
if (!req.body.email) {
return res.status(422).send({ error: 'Bad Input'})
}
const email = String(req.body.email)
const firstName = String(req.body.firstName)
const lastName = String(req.body.lastName)
const data = {
email,
firstName,
lastName
}
const db = admin.firestore()
const docRef = db.collection('users')
.doc(email)
.set(data, { merge: false })
.catch(err => res.status(422).send({ error: err }))
res.status(204).end();
})
}
zohoCrmHook.js
const axios = require('axios');
const functions = require('firebase-functions');
// zoho
const clientId = functions.config().zoho.client_id;
const clientSecret = functions.config().zoho.client_secret;
const refreshToken = functions.config().zoho.refresh_token;
const baseURL = 'https://accounts.zoho.com';
module.exports = async (req, res) => {
const newLead = {
'data': [
{
'Email': req.body.email,
'Last_Name': req.body.lastName,
'First_Name': req.body.firstName,
}
],
'trigger': [
'approval',
'workflow',
'blueprint'
]
};
const { data } = await getAccessToken();
const accessToken = data.access_token;
const leads = await getLeads(accessToken);
const result = checkLeads(leads.data.data, newLead.data[0].Email);
if (result.length < 1) {
try {
return res.json(await createLead(accessToken, newLead));
}
catch (e) {
console.log(e);
}
}
else res.json({ message: 'Lead already in CRM' })
}
function getAccessToken () {
const url = `https://accounts.zoho.com/oauth/v2/token?refresh_token=${refreshToken}&client_id=${clientId}&client_secret=${clientSecret}&grant_type=refresh_token`;
return new Promise((resolve, reject) => {
axios.post(url)
.then((response) => {
return resolve(response);
})
.catch(e => console.log(e))
});
}
function getLeads(token) {
const url = 'https://www.zohoapis.com/crm/v2/Leads';
return new Promise((resolve, reject) => {
axios.get(url, {
headers: {
'Authorization': `Zoho-oauthtoken ${token}`
}
})
.then((response) => {
return resolve(response);
})
.catch(e => console.log(e))
})
}
function createLead(token, lead) {
const url = 'https://www.zohoapis.com/crm/v2/Leads';
return new Promise((resolve, reject) => {
const data = JSON.stringify(lead);
axios.post(url, data, {
headers: {
'Authorization': `Zoho-oauthtoken ${token}`
}
})
.then((response) => {
console.log(response.data)
return resolve(response);
})
.catch(e => reject(e))
})
}
function checkLeads(leads, currentLead) {
return leads.filter(lead => lead.Email === currentLead)
}
Since you're exporting two functions.https.onRequest declarations, you'll end up with two Cloud Functions, each with their own URL/endpoint. So if that's what you need, you'll need to configure two web hooks that call these functions.
From reading your question however, it sounds more like you want a single Cloud Function that does two things, in which case you should only have one functions.https.onRequest declaration that then calls two regular JavaScript functions (for example).
So something more like:
exports.myWebHook = functions.https.onRequest(function(req, res) {
zohoCrmHook(...);
createEmailList(...);
})
You'll need to figure out what to pass into the two function calls here, as you can't pass the request and response along.
Alternatively you can call the two Cloud Functions from here, but that typically just drives up your cost with little benefit.

Categories