Get data using await async without try catch - javascript

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) {
...
}
}

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.

Execution doesn't go past the second fetch call (Gatsby related stuff)

I am generating data for querying with gatsby's sourceNodes api.
The problem is that inside the getHooksInfo call something weird happens.
I do the first await fetch(rawUrl)(i am using node-fetch, but it doesn't matter that much) request, everything's fine. But when i do the second request const res = await fetch(url) inside for (const guess of guesses) it... does nothing. I see the first console.log('before fetch');, but not the console.log('after fetch');. No error either. It just goes straight to the return statement with result being always []. I tried debugging, but debugger jumped to the return statement also on this line. Even stepping into the actual fetch call didn't help that much. So if you smell anything (besides the code, obviously;D ) here that leads to that behavior, i'd really appreciate your help.
exports.sourceNodes = async ({ actions }) => {
const { createNode } = actions;
...
await Promise.allSettled(someArray
.map(async ({ package: { links, author, description }, score, searchScore }, i) => {
const { npm, homepage, repository } = links;
const { final, detail: { popularity } } = score;
const { ok, result } = await getHooksInfo(repository);
...
createNode(...);
}));
async function getHooksInfo(repository) {
...
const res = await fetch(rawUrl);
if (!res.ok) {
return {
ok: false,
result: null
};
}
const readme = await res.text();
const hooks = [...];
const result = [];
await Promise.allSettled(
hooks.map(async (hook) => {
for (const guess of guesses) {
...
console.log('before fetch');
const res = await fetch(someUrl);
console.log('after fetch');
...
}
})
);
return {
ok: true,
result
};
}
I'm guessing that Promise.allSettled is swallowing some syntax error in your fetch(url) and that that is why you aren't getting errors.

Nodejs promise pending

I'm trying to make an constructor for multiple Redis connections, so i've started to try something.
I'm only getting back from has Promise { }, but if I do an console.log before the return I'm getting the real Value.
EDIT: Tried without async/await still don't work.
app.js
const rBredis = require("./redis");
const redis = new rBredis();
console.log(redis.has("kek"));
redis.js
const Redis = require("ioredis");
class BasicRedis {
constructor() {
// TODO
};
redis = new Redis();
async has(id) {
return await this.redis.exists(id)
.then( exists => {
// console.log(exists); works 0
return exists; // works not Promise { <pending> }
});
};
}
module.exports = BasicRedis;
I don't understand your question completely but I see a problem here.
You need to brush up your knowledge of Promises and Async await. You either use async
await or Promises (.then) syntax to make it work properly.
redis.js
class BasicRedis {
constructor() {
// TODO
};
redis = new Redis();
// You can either do it like this
has(id) {
return new Promise((res, rej) => {
this.redis.exists(id)
.then( exists => {
res(exists)
}).catch(err => {
rej(err.message)
});
})
};
// Or like this
has(id) {
return this.redis.exists(id)
};
}
In both cases, you can await/.then result in your app.js
// app.js
const rBredis = require("./redis");
const redis = new rBredis();
redis.has("kek").then(res => console.log(res))
EDIT - 1
If this is something that'd take time even 1 millisecond there's no way you're going to get the value right away. You need to use either async-await or promises. Or use a callback like this
redis.js
class BasicRedis {
constructor() {
// TODO
};
redis = new Redis();
has(id, callback) {
this.redis.exists(id)
.then( exists => {
callback(exists)
}).catch(err => {
callback(err.message)
});
};
}
app.js
const rBredis = require("./redis");
const redis = new rBredis();
redis.has("kek", (res) => console.log(res))
Here's reference to Promises MDN and Async Await MDN
Hope it helps.

How to await Function to finish before executing the next one?

// The Below code already contains the suggestions from the answers and hence works :)
within the below script I tried to fully execute the 'createDatabase' function before the .then call at the end starts to run. Unfortunately I couldn't figure out a solution to achieve just that.
In generell the flow should be as followed:
GetFiles - Fully Execute it
CreateDatabase - Fully Execute it (while awaiting each .map call to finish before starting the next)
Exit the script within the .then call
Thanks a lot for any advise :)
const db = require("../database")
const fsp = require("fs").promises
const root = "./database/migrations/"
const getFiles = async () => {
let fileNames = await fsp.readdir(root)
return fileNames.map(fileName => Number(fileName.split(".")[0]))
}
const createDatabase = async fileNumbers => {
fileNumbers.sort((a, b) => a - b)
for (let fileNumber of fileNumbers) {
const length = fileNumber.toString().length
const x = require(`.${root}${fileNumber.toString()}.js`)
await x.create()
}
}
const run = async () => {
let fileNumbers = await getFiles()
await createDatabase(fileNumbers)
}
run()
.then(() => {
console.log("Database setup successfully!")
db.end()
process.exitCode = 0
})
.catch(err => {
console.log("Error creating Database!", err)
})
The x.create code looks as follows:
const dbQ = (query, message) => {
return new Promise((resolve, reject) => {
db.query(query, (err, result) => {
if (err) {
console.log(`Error: ${err.sqlMessage}`)
return reject()
}
console.log(`Success: ${message}!`)
return resolve()
})
})
}
x.create = async () => {
const query = `
CREATE TABLE IF NOT EXISTS Country (
Code CHAR(2) UNIQUE NOT NULL,
Flag VARCHAR(1024),
Name_de VARCHAR(64) NOT NULL,
Name_en VARCHAR(64) NOT NULL,
Primary Key (Code)
)`
const result = await dbQ(query, "Created Table COUNTRY")
return result
}
If you want each x.create to fully execute before the next one starts, i.e. this is what I interpret where you say while awaiting each .map call to finish before starting the next - then you could use async/await with a for loop as follows:
const createDatabase = async fileNumbers => {
fileNumbers.sort((a, b) => a - b);
for (let fileNumber of fileNumbers) {
const x = require(`.${root}${fileNumber.toString()}.js`);
await x.create();
})
}
However, this also assumes that x.create() returns a Promise - as you've not shown what is the typical content of .${root}${fileNumber.toString()}.js file is, then I'm only speculating
The other interpretation of your question would simply require you to change createDatabase so that promises is actually an array of promises (at the moment, it's an array of undefined
const createDatabase = async fileNumbers => {
fileNumbers.sort((a, b) => a - b);
const promises = fileNumbers.map(fileNumber => {
const x = require(`.${root}${fileNumber.toString()}.js`);
return x.create();
})
await Promise.all(promises);
}
Now all .create() run in "parallel", but createDatabase only resolves onces all promises returned by x.create() resolve - again, assumes that x.create() returns a promise

Test failing after adding then and catch to promise

I have the following (simplified for the sake of scoping down problem) code:
function pushPromises() {
const promises = [];
promises.push(firstPromise('something'))
promises.push(secondPromise('somethingelse'))
return promises;
}
export default handlePromises(async (c) => {
const results = await Promise.all(pushPromises())
c.success(results);
});
My test mocks those firstPromise and secondPromise to check if they were called with the right arguments. This works (assume mock set up is properly done):
jest.mock('src/firstPromise');
jest.mock('src/secondPromise');
describe('test', () => {
let firstMock;
let secondMock;
beforeEach(() => {
require('src/firstPromise').default = firstMock;
require('src/secondPromise').default = secondMock;
})
it('test', async () => {
await handlePromises(context);
expect(firstPromiseMock).toHaveBeenCalledTimes(1);
expect(secondPromiseMock).toHaveBeenCalledTimes(1);
});
});
Now, if I add handlers to the promises such as:
function pushPromises() {
const promises = [];
promises.push(
firstPromise('something')
.then(r => console.log(r))
.catch(e => console.log(e))
)
promises.push(
secondPromise('somethingelse')
.then(r => console.log(r))
.catch(e => console.log(e))
)
return promises;
}
The first expectation passes, but the second one doesn't. It looks like the second promise is no longer called.
How can I modify the test/code so that adding handlers on the promises don't make the test break? It looks like it is just finishing the execution on the first promise handler and does never get to the second one.
EDIT:
I have modified the function to return a Promise.all without await:
export default handlePromises(async (c) => {
return Promise.all(pushPromises())
});
But I'm having the exact same issue. The second promise is not called if the first one has a .then.
In your edit, your handlePromises is still not a promise..
Try the following. ->
it('test', async () => {
await Promise.all(pushPromises());
expect(firstPromiseMock).toHaveBeenCalledTimes(1);
expect(secondPromiseMock).toHaveBeenCalledTimes(1);
});
Your handlePromises function accepts a callback but you are handling it as it returns a promise, it is not a good way to go but you can test your method using callback as following
Modify your Test as=>
it('test', (done) => {
handlePromises(context);
context.success = (results) => {
console.log(results)
expect(firstPromiseMock).toHaveBeenCalledTimes(1);
expect(secondPromiseMock).toHaveBeenCalledTimes(1);
done();
}
});
Or modify your code as =>
function pushPromises() {
const promises = [];
promises.push(firstPromise('something'))
promises.push(secondPromise('somethingelse'))
return promises;
}
export default handlePromises = (context) => {
return Promise.all(pushPromises()).then((data) => context.success(data))
};
//with async
export default handlePromises = async (context) => {
let data = await Promise.all(pushPromises());
context.success(data)
};

Categories

Resources