Promising an async function - javascript

I have an array of objects that I'm looping over and calling a function with them as the argument. It's an async/await function, and I'd like to create a PromiseAll that resolves when all of the async/await calls have concluded. I've used an array map to convert them to promises, but the promises resolve instantly and don't wait until all await calls have been made.
async function runTest({_id, name, live, dev}) {
(async () => {
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
} catch(error) {
await browser.close()
} finally {
await browser.close()
return
}
})();
}
module.exports = (tests) => {
let testPromises = tests.map((test) => {
return runTest(test).then(function (res) {
return console.log(res, 'done')
})
});
Promise.all(testPromises).then((data) => {
console.log('Done resolving')
}).catch(function(err){
})
}
What is the correct way to guarantee that all of the array objects have passed through the function and completed processing before resolving the PromiseAll? I'm not hugely familiar with async/await.

You dont need an async IIFE as i already mentioned in comments. More over your code can be simplified like so:
async function runTest({_id, name, live, dev}) {
// we can have one try/catch since you close browser at any error
try{
const browser = await puppeteer.launch();
const page = await browser.newPage();
}
catch(error) {
return await browser.close()
}
}
module.exports = async (tests) => {
try{
const data = await Promise.all(tests.map(test => runTest(test)));
console.log('Done resolving')
}
catch(e){ console.log(e)}
}

I think you may be missing a return statement in runTest, though I don't know why you need the anonymous async function in the first place, like #TheReason mentioned:
async function runTest({
_id,
name,
live,
dev
}) {
return (async () => { // here
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
} catch (error) {
await browser.close()
} finally {
await browser.close()
return
}
})();
}

The straightforward approach:
function launch() {
return new Promise(function(resolve, reject) {
setTimeout(resolve, 5000, 'blast off!')
})
}
async function doSomething(id) {
const message = await launch()
return `${message} [${id}]`
}
let runs = [10, 20, 30].map(doSomething)
Promise.all(runs).then(function(values) {
console.log(values)
})
//outputs
[ 'blast off! [10]', 'blast off! [20]', 'blast off! [30]' ]

Related

Factory function for mongoose transaction session on Typescript

I have a lot of function like these
currency.service.ts
async createCurrencyOnSession(createCurrencyDto: CreateCurrencyDto): Promise<ResponseCurrencyDto> {
const session = await this.currencyModel.startSession();
session.startTransaction();
try {
const currency = await this.createCurrency(createCurrencyDto, {session});
await session.commitTransaction();
return currency;
} finally {
await session.endSession();
}
}
user.service.ts
async createUserOnSession(createUserDto: CreateUserDto): Promise<ResponseUserDto> {
const session = await this.userModel.startSession();
session.startTransaction();
try {
const user = await this.createUser(createUserDto, {session});
await session.commitTransaction();
return user;
} finally {
await session.endSession();
}
}
I don't like many try catch on code so I try to edit
currency.service.ts
async createCurrencyOnSession(
createCurrencyDto: CreateCurrencyDto,
): Promise<CurrencyResponseDto> {
const session = await this.currencyModel.startSession();
session.startTransaction();
return handleSession(this.createCurrency(createCurrencyDto, { session }));
}
export const handleSession =
async (handler) => async (dto, options: ISessionOptions) => {
try {
return await handler(dto, options);
} finally {
await options.session.endSession();
}
};
I can see error on Typescript because the main function return a specific interface: CurrencyResponseDto, UserResponseDto. How can I add dynamic interface to return CurrencyResponseDto, UserResponseDto, ... on factory function.
Could you help me to make it clean or suggest a better version on problem. Thanks

JavaScript NodeJS How to use stream/promises with async functions?

I have a JS async function in Node. Say, it downloads a file from a URL and does something with it, eg. unzip it. I wrote it like this, it works, but eslint showed me there is a thick code smell: error Promise executor functions should not be async no-async-promise-executor. Async was required because of await fetch in body function.
I am not skilled enough with streams nor async/await to correct it by myself. I would like to get rid of the Promise and fully use async/await. The module stream/promises seems the way to go from Node-15 on as commented here how-to-use-es8-async-await-with-streams. How to use await pipeline(...) in this context? Maybe there's a better and shorter way?
Here's the function:
function doSomething(url) {
return new Promise(async (resolve, reject) => {
try {
const fileWriteStream = fs.createWriteStream(someFile, {
autoClose: true,
flags: 'w',
});
const res = await fetch(url);
const body = res.body;
body
.pipe(fileWriteStream)
.on('error', (err) => {
reject(err);
})
.on('finish', async () => {
await doWhatever();
resolve('DONE');
});
} catch (err) {
reject(err);
}
});
}
You could simply perform the await before getting to the executor:
async function doSomething(url) {
const fileWriteStream = fs.createWriteStream(someFile, { autoClose: true, flags: 'w' });
let { body } = await fetch(url);
body.pipe(fileWriteStream);
return new Promise((resolve, reject) => {
body.on('error', reject);
body.on('finish', resolve);
});
};
My advice in general is to remove as much code from within your promise executors as possible. In this case the Promise is only necessary to capture resolution/rejection.
Note that I've also removed doWhatever from within doSomething - this makes doSomething much more robust. You can simply do:
doSomething('http://example.com').then(doWhatever);
Lastly I recommend you set someFile as a parameter of doSomething instead of referencing it from some broader context!
To use the pipeline function you're looking for, it would be
const { pipeline } = require('stream/promises');
async function doSomething(url) {
const fileWriteStream = fs.createWriteStream(someFile, {
autoClose: true,
flags: 'w',
});
const res = await fetch(url);
await pipeline(res.body, fileWriteStream);
await doWhatever();
return 'DONE';
}
You can use the fs/promises in NodeJS and trim your code down to the following:
import { writeFile } from 'fs/promises'
async function doSomething(url) {
const res = await fetch(url);
if (!res.ok) throw new Error('Response not ok');
await writeFile(someFile, res.body, { encoding: 'utf-8'})
await doWhatever();
return 'DONE';
});
}

How to console.log the data properly with fetch?

What's the proper way of returning the data not the promise object as the code snippet below does?
const getData = async () => {
try {
const res = await fetch(API_ENDPOINT);
const data = res.json();
return data;
} catch (error) {
ToastAndroid.show(error, ToastAndroid.LONG);
}
};
console.log(getGeneralData());
Async Functions return a promise, you need to use :
getData().then(res=>console.log(res);
You can wrap your code in "self invoked" function (async function() {....})() and it allows you to use await for awaiting result of getData().
(async function() {
const getData = async() => {
try {
const res = await fetch(API_ENDPOINT);
const data = res.json();
return data;
} catch (error) {
ToastAndroid.show(error, ToastAndroid.LONG);
}
};
console.log(await getData());
})();
The async function is just syntactic sugar for a Promise. You can call the .then() method to handle resolving the promise.
const API_ENDPOINT = 'https://jsonplaceholder.typicode.com/posts/1/comments';
const getData = async () => {
try {
const res = await fetch(API_ENDPOINT);
return res.json();
} catch (error) {
ToastAndroid.show(error, ToastAndroid.LONG);
}
};
getData().then(data => console.log(data));
.as-console-wrapper { top: 0; max-height: 100% !important; }

Using async await with axios

I've got a function that makes a call with axios and returns it:
const makeRequest = () => {
return axios.get('/data');
}
const printResponse = () => {
makeRequest()
.then((response) => {
console.log(response)
})
.catch((error) => {
console.log(error)
})
}
I'm trying to use async await to make this better and avoid using 'then'.
const makeRequest = async () => {
return await axios.get('/data');
}
const printResponse = () => {
try {
const response = makeRequest();
console.log(response)
} catch(error) {
console.log(error)
}
}
However, despite using await, makeRequest still returns a promise, so I have to end up using then in my printResponse function anyway. Am I not using this correctly?
"However, despite using await, makeRequest still returns a promise"
Yes, of course it does. async/await is just syntactic sugar for working with Promises. In particular, any function marked async will return a Promise - this is by design.
If you don't like using .then, you can just as easily use async/await to consume this promise:
const printResponse = async () => {
try {
const response = await makeRequest();
console.log(response)
} catch(error) {
console.log(error)
}
}

Using await in then callback - the keyword 'await' is reserved

In node.js, I have a database transaction, where I want to call an async method in then callback, but I get error message the keyword 'await' is reserved.
This is async saveImage function:
const saveImage = async (parsedLink) => {
AWS.config.region = config.awsSettings.region;
AWS.config.accessKeyId = config.awsSettings.accessKeyId;
AWS.config.secretAccessKey = config.awsSettings.secretAccessKey;
const bucket = new AWS.S3({
params: {
Bucket: config.awsSettings.images_bucket_name,
},
});
const currentDateString = new Date().toISOString().replace(/\:|\./g, '-');
const bodystream = new Buffer(parsedLink.imgUrl, 'binary');
const imageUrlDomain = parseDomain(parsedLink.linkUrl).domain;
const params = {
Key: `${parsedLink.id}/${imageUrlDomain}_${currentDateString}${parsedLink.imgType}`,
ContentType: parsedLink.imageMime,
ContentEncoding: 'base64',
Body: bodystream,
};
const resultPromise = await bucket.upload(params).promise();
return resultPromise.Location;
};
If I want to use saveImage function, I get the error message.
module.exports.addTestObject = async (ctx) => {
const testObj = ctx.request.body;
try {
switch (testObj.type) {
case interestT.getType.link: {
const knexTestObject = TestObject.knex();
transaction(knexTestObject, trx =>
TestObject.query(trx)
.insert({
interestDate: testObj.date,
})
.then(newInterest => {
// save image
if (parsedLink.saveImg) {
parsedLink.imgUrl = await saveImage(testObj);
}
newInterest.$relatedQuery('linkInterestsRel', trx).insert({
linkHeader: testObj.linkHeader,
}),
}
),
)
.then((linkInterest) => {
console.log(linkInterest);
})
.catch((err) => {
throw err;
});
break;
}
default:
break;
}
ctx.response.body = interestObj;
} catch (err) {
const statusCode = err.status || 400;
ctx.throw(statusCode, err.message);
}
};
Regular functions run synchronously till they return. Therefore you cannot use await inside them as you cannot wait for an asynchronous event in a synchronous way.
JavaScript also has async functions, which look like regular functions, but are conceptually quite different: They run synchronously till they reach an await, then they stop and continue once the awaited Promise resolves. As such they cannot return their result synchronously, instead they return a Promise which then resolves when the function finished execution.
Therefore you need to convert your function into an async function:
async function getUsername() { // <-- async keyword here
return (await getUser()).name; // <-- await can be used inside
}
Now this does also work inside a .then callback:
getUser().then(async function(user) {
const friends = await getFriends(user);
// ...
})
But this somewhat mixes the abstraction async functions with their underlying primitive Promise. If you would just await the Promise instead of adding a .then callback, the code gets way more readable:
(async function() {
const user = await getUser();
const friends = await getFriends(user);
})();
The concrete question could be rewritten as:
const linkInterest = await transaction(knexTestObject, async trx => {
const newInterest = await TestObject.query(trx)
.insert({ interestDate: testObj.date, });
if (parsedLink.saveImg) {
parsedLink.imgUrl = await saveImage(testObj);
}
await newInterest.$relatedQuery('linkInterestsRel', trx)
.insert({ linkHeader: testObj.linkHeader, }),
});

Categories