Mongoose Object.save() only working if called twice - javascript

I know this sounds rediculess...
In my function index() I retrieve a Model for a specific collectionName promted by commandline parameters. After a async AXIOS API call a saveToDB(response, model) is called.
export async function index(collectionName) {
let model = getModelForCollection(collectionName)
await axios.get(url)
.then(resp => {
saveToDB(resp.data, model)
.then(saved => {
...
})
})
}
async function saveToDB(response, model) {
const word = new model({response})
await word.save()
.then(() => {
console.log("saved")
})
}
So basicly this should easily work, but the strange thing is. This does only! work, if I save another document prior to calling saveToDB() in index(). Changing the function to;
export async function index(collectionName) {
let model = getModelForCollection(collectionName)
let word = new model({word: 'WORT'})
await word.save().then(() => {
console.log("saved")
})
await axios.get(url)
.then(resp => {
saveToDB(resp.data, model)
.then(saved => {
...
})
})
}
Is there anything strange going on I am not aware of? Excuse my super unspecific question.

Maybe you should read the docu: https://mongoosejs.com/
or search for related questions. I guess its not a mongoos problem.

Why are you consuming and "awaiting" the Promises at the same time. Why don't you keep it simple like this:
export async function index(collectionName) {
try {
let model = getModelForCollection(collectionName)
let resp = await axios.get(url);
let saved = await saveToDB(resp.data, model);
} catch (error) {
console.log("Error");
console.log(error);
}
}
async function saveToDB(response, model) {
try {
const word = new model({ response });
let writeStatus = await word.save();
console.log("saved");
} catch (error) {
console.log("Error");
console.log(error);
}
}

Related

Promises not getting resolved

I am running this asynchronous function in my React app -
const getMetaData = async (hashes: any) => {
console.log({ hashes });
try {
const data = hashes.map(async (hash: any) => {
const url = `http://localhost:3003/user/pinata/getmetadata/${hash}`;
const metadata = await axios.get(url);
return metadata.data.response;
});
console.log("data1", data);
const metadata = await Promise.all(data);
console.log('data2', metadata);
} catch (error) {
console.log('getMetaData Error', error);
}
};
console.log("data1", data) gives me -
data1 (12) [Promise, Promise, Promise, Promise, Promise, Promise, Promise, Promise, Promise, Promise, Promise, Promise]
The problem here is after I do a await Promise.all(data) I don't get data2 anywhere in the console. Maybe because the Promises are not even getting resolved?
Any idea what might be wrong?
Thanks in advance.
It seems that your code works fine when using SWAPI API so it can be that the API you use does not deliver data appropriately. I run the below code to test. Here's a link to codebox to play around with it if you want.
import axios from "axios";
const data = ["people", "planets", "starships"];
const getMetaData = async (hashes) => {
console.log({ hashes });
try {
const data = hashes.map(async (hash) => {
const url = `https://swapi.dev/api/${hash}`;
const metadata = await axios.get(url);
return metadata.data.results;
});
console.log("data1", data);
const metadata = await Promise.all(data);
console.log("data2", metadata);
} catch (error) {
console.log("getMetaData Error", error);
}
};
getMetaData(data);
With this code, it appears the most likely situation is that one of the promises in the loop is not resolving or rejecting. To confirm that, you can log every possible path with more local error handling so you can see exactly what happens to each request. I also added a timeout to the request so you can definitely find out if it's just not giving a response, but you can also see that by just looking at the logging for begin and end of each request in the loop:
function delay(msg, t) {
return new Promise((resolve, reject)) => {
setTimeout(() => {
reject(new Error(msg));
}), t);
});
}
const getMetaData = async (hashes: any) => {
console.log({ hashes });
try {
const data = hashes.map(async (hash: any, index: number) => {
try {
console.log(`Request: ${index}, hash: ${hash}`);
const url = `http://localhost:3003/user/pinata/getmetadata/${hash}`;
const metadata = await axios.get(url);
console.log(`Request: ${index}, result: ${metadata.data.response}`);
return metadata.data.response;
} catch (e) {
console.log(`Request: ${index} error: `, e);
throw e;
}
});
console.log("data1", data);
const metadata = await Promise.all(data.map((p: any, index: number) => {
return Promise.race(p, delay(`Timeout on request #${index}`, 5000));
});
console.log('data2', metadata);
} catch (error) {
console.log('getMetaData Error', error);
}
};
FYI, I don't really know Typescript syntax so if I've made any Typescript mistakes here, you can hopefully see the general idea and fix the syntax.

JavaScript function(callback).then(), callback executes after then

I have trouble to get this to work.
I have a function getItem:
export const getItem = async (key, callback) => {
value = await Expo.SecureStore.getItemAsync(key).catch((error) => console.log(error));
callback(value);
}
getItem is supposd to get a token and pass that token to the callback.
Now I want to use getItem in this (simplified) class:
export class Post {
constructor(){
this.token = false;
}
post() {
console.log('Token: ' + this.token);
...
}
setToken(token){
console.log('Set token.');
this.token = token;
}
authorizedPost() {
getItem('token', this.setToken.bind(this)).then(
this.post()
);
}
}
I use this class like this:
let post = new Post();
post.authorizedPost();
This is the output I get:
Token: false
Set token.
But I need the token to be set and after that, I want to call the method this.post();
Since I'm a beginner, I want to excuse if that issue is trivial. But I am thankful for every help!
I had to pass a function to then like this:
authorizedPost() {
getItem('token', this.setToken.bind(this)).then(
()=>this.post()
);
}
I know you didn't really ask, but…
Part of the reason the code is difficult to debug is because you are using callbacks when you don't need to and mixing them with promises. These are two different async paradigms that are normally better off separate. You can get rid of the callback in your code in a way that will make it much more readable.
Expo.SecureStore.getItemAsync()
returns a promise, so just return it.
const getItem = (key) => Expo.SecureStore.getItemAsync(key);
Then in your method you can call then and simply call the function you were passing as a callback. No need for callback or bind. Just one line after the other:
authorizedPost() {
getItem('token').then(val => {
this.setToken(val) // the order is now obvious
this.post()
})
.catch((error) => console.log(error))
}
Here's a snippet with a faked Expo method:
let Expo = {
SecureStore: {
getItemAsync() {
return new Promise((resolve, reject) => setTimeout(() => resolve("someFancyToken"), 1000))
}
}
}
class Post {
constructor() {
this.token = false;
}
post() {
console.log('Token: ' + this.token);
}
setToken(token) {
console.log('Set token.');
this.token = token;
}
authorizedPost() {
getItem('token').then(val => {
this.setToken(val)
this.post()
})
.catch((error) => console.log(error))
}
}
const getItem = (key) => Expo.SecureStore.getItemAsync(key);
let post = new Post();
post.authorizedPost();

How to properly implement mongodb async/await inside a promise?

I've read that having an async inside a Promise is anti-pattern for async/await. The code below works, but I am curious how else to achieve the same result without having async in Promise.
If I remove it, the linter would tell how I can't use await in my mongodb query. If I remove the await in the mongodb query, then it wouldn't wait for the result.
export const getEmployees = (companyId) => {
return new Promise(async (resolve, reject) => {
const employees = await Employees.find(
{ companyId },
);
// other logic here...
resolve({
employees,
});
});
Thanks.
async functions automatically return Promises already, which resolve with whatever expression is eventually returned. Simply make getEmployees an async function:
export const getEmployees = async (companyId) => {
const employees = await Employees.find(
{ companyId },
);
// other logic here...
return { employees };
};
(but make sure to catch in the consumer of getEmployees just in case there's an error)
As #CertainPerformance answered, that is perfect way to retrieve data from mongoDB using async/await, I would like to add some more information about how to handle errors in this case for correctness of the system, and better error handle to return better status to the client about his request.
I'd say it , you usually want to catch all exceptions from async/await call.
try {
const employees = await Employees.find({
companyId
});
// You can add more logic here before return the data.
return {
employees
};
} catch (error) {
console.error(error);
}
Now let's check the ways we can handle our errors that might occur.
Handle error inside error scope.
Assign a default value to the variable in the catch block.
Inspect error instance and act accordingly.
This is the most common way to handle errors in those cases and most elegant way in my opinion.
Handle error inside error scope:
export const getEmployees = async (companyId) => {
try {
const employees = await Employees.find({
companyId
});
// You can add more logic here before return the data.
return {
employees
};
} catch (error) {
console.error(error);
}
};
Assign a default value to the variable in the catch block:
export const getEmployees = async (companyId) => {
let employees;
try {
employees = await Employees.find({
companyId
});
// You can add more logic here before return the data.
employees = employees;
} catch (error) {
console.error(error);
}
if (employees) { // We received the data successfully.
console.log(employees)
// Business logic goes here.
}
return employees;
};
Inspect error instance and act accordingly:
export const getEmployees = async (companyId) => {
try {
const employees = await Employees.find({
companyId
});
// You can add more logic here before return the data.
return {
employees
};
} catch (error) {
if (error instanceof ConnectionError) {
console.error(error);
} else {
throw error;
}
}
};
Some more explanations about async await and more useful methods that you can find in those answers.
How run async / await in parallel in Javascript

Get data using await async without try catch

I am trying to use await-async without try-catch for this:
const getUsers = async (reject, time) => (
new Promise((resolve, reject) => {
setTimeout(() => {
if (reject) {
reject(....)
}
resolve(.....);
}, time);
})
);
module.exports = {
getUsers ,
};
With try-catch block it looks like this:
const { getUsers } = require('./users');
const users = async () => {
try {
const value = await getUsers(1000, false);
.....
} catch (error) {
.....
}
}
users();
How can I write the same code without using the try-catch block?
Using the promise functions then-catch to make the process simpler I use this utils :
// utils.js
const utils = promise => (
promise
.then(data => ({ data, error: null }))
.catch(error => ({ error, data: null }))
);
module.exports = utils;
And then
const { getUsers } = require('./api');
const utils = require('./utils');
const users = async () => {
const { error, data } = await utils(getUsers(2000, false));
if (!error) {
console.info(data);
return;
}
console.error(error);
}
users();
Without using the try-catch block I got the same output, this way makes it better to understand the code.
In Extension to L Y E S - C H I O U K H's Answer:
The Utils Function is actually correct but, make sure to add the return keyword before the promise as shown down below:
// utils.js
const utils = promise => (
return promise
.then(data => { [data, null]; })
.catch(error => { [null, error]; });
);
module.exports = utils;
When Calling in Main Code:
let resonse, error; // any variable name is fine make sure there is one for error and the response
[response, error] = await utils(any_function()); // Make sure that inside the tuple, response is first and error is last like: [response, error].
if (error) console.log(error);
// -- Do Whatever with the Response -- //
Using My Method Would Give you Benefits like:
Your Own Variable Names.
Not Running into Type Safety issues when using Typescript.
Good Reason to Strong Type your code.
Personally, I have been using this in my code lately, and has reduced some many headaches, my code is cleaner, I don't have to stick with the same variable names, especially when working on a large codebase.
Happy Coding :)
See Ya!
If you have a valid default for the error case you can use the catch method on the getUsers promise and then await a promise whose error will be handled
const users = async () => {
const value = await getUsers(1000, false).catch(e => null);
}
While this approach should work it should be noted that this may mask the case when getUsers returns null vs when it raises an error, and you will still need to check for the null or get a null access error. All in all I would stick with the try { .. } catch (e) { ... } for most casses
A package I found called await-to-js can also help it.
import to from 'await-to-js';
const [err, users] = await to(getUsers());
if(err) doSomething();
The idea is like Lyes CHIOUKH's method, just a wrapper. Copied the source code here.
/**
* #param { Promise } promise
* #param { Object= } errorExt - Additional Information you can pass to the err object
* #return { Promise }
*/
export function to<T, U = Error> (
promise: Promise<T>,
errorExt?: object
): Promise<[U | null, T | undefined]> {
return promise
.then<[null, T]>((data: T) => [null, data])
.catch<[U, undefined]>((err: U) => {
if (errorExt) {
Object.assign(err, errorExt);
}
return [err, undefined];
});
}
export default to;
If you have such above single line async/await function, then this would have been clean code for you:
const getUsers = async (time, shouldReject=false) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (shouldReject) {
reject(Error('Rejected...'));
} else {
resolve(["User1", "User2"]);
}
}, time);
});
}
const userOperation = users => {
console.log("Operating user", users);
}
// Example 1, pass
getUsers(100)
.then(users => userOperation(users))
.catch(e => console.log(e.message));
// Example 2, rejected
getUsers(100, true)
.then(users => userOperation(users))
.catch(e => console.log(e.message));
And for multiple await in a single async function, it would good to have try/catch block as below:
const users = async () => {
try {
const value = await getUsers(1000, false);
const value1 = await getUsers2(1000, false);
...
} catch (error) {
...
}
}

Save Async/Await response on a variable

I am trying to understand async calls using async/await and try/catch.
In the example below, how can I save my successful response to a variable that can be utilized throughout the rest of the code?
const axios = require('axios');
const users = 'http://localhost:3000/users';
const asyncExample = async () =>{
try {
const data = await axios(users);
console.log(data); //200
}
catch (err) {
console.log(err);
}
};
//Save response on a variable
const globalData = asyncExample();
console.log(globalData) //Promise { <pending> }
1) Return something from your asyncExample function
const asyncExample = async () => {
const result = await axios(users)
return result
}
2) Call that function and handle its returned Promise:
;(async () => {
const users = await asyncExample()
console.log(users)
})()
Here's why should you handle it like this:
You can't do top-level await (there's a proposal for it though);
await must exist within an async function.
However I must point out that your original example doesn't need async/await
at all; Since axios already returns a Promise you can simply do:
const asyncExample = () => {
return axios(users)
}
const users = await asyncExample()
try..catch creates a new block scope. Use let to define data before try..catch instead of const, return data from asyncExample function call
(async() => {
const users = 123;
const asyncExample = async() => {
let data;
try {
data = await Promise.resolve(users);
} catch (err) {
console.log(err);
}
return data;
};
//Save response on a variable
const globalData = await asyncExample();
console.log(globalData);
// return globalData;
})();
I had same issue with you and found this post. After 2 days of trying I finally found a simple solution.
According to the document of JS, an async function will only return a Promise object instead of value. To access the response of Promise, you have to use .then()method or await which can return the resulting object of Promise is instead of Promise itself.
To change variables from await, you have access and change the variable you want to assign within the async function instead of return from it.
//Save response on a variable
var globalData;
const asyncExample = async () =>{
try {
const data = await axios(users);
globalData = data; // this will change globalData
console.log(data); //200
}
catch (err) {
console.log(err);
}
};
asyncExample();
But if you do this, you may get an undefined output.
asyncExample();
console.log(globalData) //undefined
Since asyncExample() is an async function, when console.log is called, asyncExample() has not finished yet, so globalData is still not assigned. The following code will call console.log after asyncExample() was done.
const show = async () => {
await asyncExample();
console.log(globalData);
}
show();
Because the events are happening asynchronously you need to tie in a callback/promise. I'm going to assume it returns a promise.
const axios = require('axios');
const users = 'http://localhost:3000/users';
const asyncExample = async () =>{
try {
const data = await axios(users);
console.log(data); //200
}
catch (err) {
console.log(err);
}
};
//Save response on a variable
const globalData = asyncExample().then( (success, err) => {
if (err) { console.error(err); }
console.log(success)
}
Just use a callback/promise (cascading programming):
axios(users).then(function(response) {
const globalData = response;
console.log(globalData)
});

Categories

Resources