Javascript Promise resolve confusion - javascript

private runMiddlewares(route: Route | ExceptionRoute, request: HttpRequest, response: HttpResponse): Promise<any> {
return new Promise((resolve, reject) => {
try {
route.middlewares.forEach(async (middleware: IMiddleware) => {
console.log('begin run middleware');
await middleware.process(request, response);
console.log('resolve run middleware');
console.log(request.body);
});
console.log('resolve all runMiddlewares');
resolve();
} catch (e) {
reject(e);
}
});
}
I have written this function runMiddlewares which should ideally resolve() when all the middleware.process() have resolved. I am using typescript await functionality but it doesn't seem to be working.
I expect something like this to happen inside route.middlewares.forEach(
'begin run middleware'
then awaits resolves
'resolve run middleware'
This continues for all of the middlewares in forEach loop and when the list is all done then and only then 'resolve all runMiddlewares' should be printed and finally, private runMiddlewares( ... ) should resolve.
But instead, forEach is now getting immediately resolved thus preventing all of the middlewares to even complete.
How should this be handled? I thought that await will take care of it inside the forEach loop and only then resolve of runMiddlewares will be called in the end.
What am I missing here?

You can use map to create an array of promises from your middlewares. This array of promises can ben handed to Promise.all which resolves when every single promise of the array has been resolved.
await Promise.all(route.middlewares.map((middleware: IMiddleware) => {
console.log('begin run middleware');
const promise = middleware.process(request, response);
console.log('resolve run middleware');
console.log(request.body);
return promise
});
Or even more compact:
runMiddlewares(...) {
return Promise.all(
route.middlewares.map((middleware: IMiddleware) => {
return middleware.process(request, response))
})
)
}

So, following the recommendation ( https://cn.eslint.org/docs/3.0.0/rules/no-await-in-loop ), I wrote it as follows
private runMiddlewares(route: Route | ExceptionRoute, request: HttpRequest, response: HttpResponse): Promise<any> {
return new Promise(async (resolve, reject) => {
try {
const middlewarePromiseArry: any[] = [];
route.middlewares.forEach((middleware: IMiddleware) => {
console.log('begin run middleware');
middlewarePromiseArry.push(middleware.process(request, response));
// removed the use of await inside of forEach loop following this link: https://cn.eslint.org/docs/3.0.0/rules/no-await-in-loop
// await middleware.process(request, response);
// console.log('resolve run middleware');
// console.log(request.body);
});
await Promise.all(middlewarePromiseArry);
console.log('resolve all runMiddlewares');
resolve();
} catch (e) {
reject(e);
}
});
}
I am happy to accept further answers and recommendations/improvements :)

Related

JavaScript: Using Async/Await in a Promise

On the way of learning the concepts of asynchronous JavaScript, I got struggled with the idea behind the situation when they can be chained. As an example consider the following situation: a webhook calls a cloud function and as a requirement there is set a time interval by which the cloud function should response to the webhook. In the cloud function is called an operation for fetching some data from a database, which can be short- or long-running task. For this reason we may want to return a promise to the webhook just to "register" a future activity and later provide results from it e.g.:
async function main () {
return new Promise ((resolve, reject) => {
try {
db.getSomeData().then(data=> {
resolve({ result: data});
});
} catch (err) {
reject({ error: err.message });
}
});
}
Another way is to use async/await instead of a promise for fetching the data, e.g.:
async function main () {
return new Promise (async (resolve, reject) => {
try {
const data = await db.getSomeData();
resolve({ result: data });
} catch (err) {
reject({ error: err.message });
}
});
}
(The code is a pseudo-code and therefore all checks on the returned types are not considered as important.)
Does the await block the code in the second example and prevent returning of the promise?
async functions always return a Promise. It is up to the function caller to determine how to handle it: either by chaining with then/catch or using await. In your example, there is no need to create and return a new Promise.
You could do something like this for example:
async function main () {
try {
const data = await db.getSomeData()
return {result: data}
} catch (err) {
return {error: err.message}
}
}
// On some other part of the code, you could handle this function
// in one of the following ways:
//
// 1.
// Chaining the Promise with `then/catch`.
main()
.then((result) => console.log(result)) // {result: data}
.catch((err) => console.log(err)) // {error: err.message}
// 2.
// Using await (provided we are working inside an asyn function)
try {
const result = await main()
console.log(result) // {result: data}
} catch (err) {
console.log(err) // {error: err.message}
}
Try to avoid combining both methods of handling asynchronous operations as a best practice.

resolve and reject issue using node js

Is this possible way to return resolve or reject message from one function to another?
As I am writing to pass resolve message in postman whenever my task is completed or reject message when there is some error
But after after writing return it still not returning the resolve message or reject message inside Postman
any idea how this can be resolve?
async function readFile(filePath) {}
async function getAllFile(filePath) {
const paths = await readFile(filePath);
}
async function filterFiles(filePath) {
const paths = await getAllFile(filePath);
}
function addDocument(childProduct){
return new Promise((resolve, reject) => {
Document.create({
name: childProduct,
},
}).then(function (filePath) {
filterFiles(filePath);
let msg = "Document created Succesfully";
return resolve(msg);
})
.catch(function (err) {
return reject("Can't be updated please try again :) " + err);
});
});
}
function updateDoc(data){
return new Promise((resolve, reject) => {
Document.update({
name: data.name,
}
where: {
product_id: data,
},
})
}).then(function (childProduct) {
addDocument(childProduct);
let msg = "Updated Successfully";
return resolve(msg);
})
.catch(function (err) {
return reject("Can't be updated please try again :) " + err);
});
}
Product.findOne and Document.findAll return a Promise, so they can be returned and awaited directly.
You can chain await func1(); await func2(); await func3() in one try{} block, and catch any error that happens in one place :
const filterFiles = async filePath => {
const paths = await getAllFiles(filePath);
// .. Do something else here
return paths // This is a Promise because async functions always return a Promise
}
const findOneDoc = name => Product.findOne({ where: { name } }); // This func returns a Promise
const findAllDocs = product_id => Document.findAll({ // This func returns a Promise too
raw: true,
where: { product_id }
});
(async () => {
try {
const childProduct = await findOneDoc("some_name");
console.log("All good until now!");
const filePath = await findAllDocs(childProduct._id);
console.log("Still good");
const filteredFiles = await filterFiles(filePath);
console.log("All went well.");
console.log(filteredFiles);
} catch (err) {
// If any of the functions above fails, the try{} block will break and the error will be caught here.
console.log(`Error!`, err);
}
})();
There are few things I would like to mention.
When you create a promise, it should have resolve() and reject() inside it.
for ex-
function testPromise() {
return new Promise((resolve, reject) => {
// your logic
// The followin if-else is not nessesary, its just for an illustration
if (Success condition met) {
resolve(object you want to return);
}else {
reject(error);
// you can add error message in this error as well
}
});
}
// Calling the method with await
let obj = await testPromise()
// OR call with then, but its better to go with await
testPromise().then((obj)=>{
// Access obj here
})
In the method which you have written, You have applied .then() method to non promise object. You have to complete the promise block first with resolve() and reject() inside it. Then you can return that promise from a function, use it in async function Or apply .then() block on it.
Also you don't need to add return statement to resolve() and reject() statement. The system will take care of it.
You can also use try catch block inside a promise. Its better to write reject() statement in catch block, if anything goes wrong.

How to return Papa-parsed CSV via promise/async-await

Can someone help me understand why this returns a pending promise, rather than the data?
async function toJson (filepath) {
const file = fs.createReadStream(filepath)
let json = new Promise((resolve, reject) => {
Papa.parse(file, {
header: true,
complete (results, file) {
resolve(results)
},
error (err, file) {
reject(err)
}
})
})
let result = await json
return result.data
}
If I change the return result.data line to console.log(result.data), it logs the data array to the console, as expected. Why won't it simply return that array?!?!
As Roamer-1888 added in the comments, async functions always return a Promise, even if you await inside it and then return the data it will return as a Promise.
In the function's caller, you will have to await the Promise or use .then() on it in order to access the delivered data.
The toJson function could be better written to just return a Promise like this
function toJson (filepath) {
const file = fs.createReadStream(filepath)
return new Promise((resolve, reject) => {
Papa.parse(file, {
header: true,
complete (results, file) {
resolve(results.data)
},
error (err, file) {
reject(err)
}
})
})
}
Now when you call toJson(), you can use either await if you are inside of an async function or chain .then() on the returned Promise to access the data.
async function main() {
try {
const data = await toJson(filepath)
// do something with the data...
} catch (err) {
console.error('Could not parse json', err)
}
}
Or with .then()
toJson('path')
.then(console.log)
.catch(console.log)
You will be able to catch errors from the underlaying FileReader (thanks to calling reject inside the error function). Keep in mind that by calling resolve with results.data you put aside results.errors and results.meta which contain useful information about the read csv.

How to return from a promise inside then block?

I'm trying to understand how promise's cascading properly works. For this, I created a function which returns a new Promise but has some callback functions in their scope:
exports.function1 = (params) => {
return new Promise((resolve, reject) => {
// do something sync
someFunctionAsyncWithCallback(params, (err, data) => { //async func
err ? reject(err) : resolve(data);
})
}).then(data => {
// do something sync again
anotherAsyncFunctionWithCallback(data, function (err, response) {
return err ? Promise.reject(err) : Promise.resolve(response);
// return err ? reject(err) : resolve(response); ?
});
})
}
Inside then block, how can I made a properly return in order to continue cascading process? In executor there are resolve/reject functions which I can call in order to continue chaining. But, once we are in then execution, these function aren't there - correct me if I'm wrong - and I don't know how to move on.
Any comment will be appreciated.
Avoid combining promise chains with callback-style APIs. Instead, wrap the callback-style API with a promise wrapper, which then lets you compose things reasonably.
The examples you've quoted look like NodeJS APIs. If you're using Node, v8 and higher have utils.promisify which can be used to quickly and easily wrap standard NodeJS-callback-style functions to functions returning promises.
// Get promise-enabled versions:
const promiseSomeFunctionAsyncWithCallback = utils.promisify(someFunctionAsyncWithCallback);
const promiseAnotherAsyncFunctionWithCallback = utils.promisify(anotherAsyncFunctionWithCallback);
// Use them:
exports.function1 = (params) => {
return promiseSomeFunctionAsyncWithCallback(params)
.then(promiseAnotherAsyncFunctionWithCallback);
})
};
If you're not using Node, or you're using an old version, there's nothing magic about utils.promisify, you can easily roll your own:
const promisify = f => return function(..args) {
return new Promise((resolve, reject) => {
f.call(this, ...args, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
};
Re your comment:
I have some sync code between these callback functions.. How would you handle it in your first example?
There are two styles for that:
1. Put the sync code in the then callback and chain only when you reach your next async bit:
exports.function1 = (params) => {
// Code here will run synchronously when `function1` is called
return promiseSomeFunctionAsyncWithCallback(params)
.then(result => {
// You culd have synchronous code here, which runs when
// this `then` handler is called and before we wait for the following:
return promiseAnotherAsyncFunctionWithCallback(result);
});
})
};
2. Put the sync code in its own then callback:
exports.function1 = (params) => {
// Code here will run synchronously when `function1` is called
return promiseSomeFunctionAsyncWithCallback(params)
.then(result => {
// You culd have synchronous code here, which runs when
// this `then` handler is called.
// Pass on the result:
return result;
})
.then(promiseAnotherAsyncFunctionWithCallback);
})
};
One advantage to #2 is that each distinct logical step is its own block. It does mean one additional yield back to the microtask loop at the end of this iteration of the main event loop, but that's not likely to be an issue.
You need to return another Promise:
return new Promise((res, rej) => anotherAsyncFunctionWithCallback(data, (err, data) => err ? rej(err) : res(data));
However then it would make sense to promisify the function:
const promisify = f => (...args) => new Promise((res, rej) => f(...args, (err, data) => err? rej(err) : res(data)));
const asyncF = promisify(AsyncFunctionWithCallback);
So one can do:
asyncF(1).then(asyncF).then(console.log);
you can use a flag variable for returning something.
example;
async function test(){
let flag=0;
await fetch(request).then(()=> flag=1}
if(flag==1) return;
}

How to resolve Web3 promises objects? [duplicate]

Im trying to use async await on a function that returns a promise but the out put im getting is Promise { <pending> }. In here im using function called convertFiletoPDF which returns a promise. I need to get the output (the path that i have mention in resolve() ).
When i use it as
convertFiletoPDF(file).then((result) => {
console.log(result);
}).catch((err)=>{
console.log(err);
});
it gives the expected result.Whats wrong with the code below? im quite new to these async await and promises.
function convertFiletoPDF(file) {
return new Promise(function(resolve, reject) {
unoconv.convert(file, "pdf", function(
err,
result
) {
if (err) {
reject(err);
}
let File = file.substring(file.lastIndexOf("/")+1,file.lastIndexOf("."));
// result is returned as a Buffer
fs.writeFile(__dirname+"/files/converted/"+File+".pdf", result, error => {
/* handle error */
if (err) reject(error);
else resolve("./files/converted/"+File+".pdf");
});
});
});
}
async function myfunc(file){
let res = await convertFiletoPDF(file);
return res;
}
let res = myfunc(file);
console.log(res);
The return value of an async function is a promise, so naturally that's what your console.log outputs. You need to either consume the result via await (within another async function) or use then/catch (within another async function).
This is what you're currently doing:
function convertFiletoPDF(file) {
return new Promise(function(resolve, reject) {
setTimeout(resolve, 400, "Done");
});
}
async function myfunc(file){
let res = await convertFiletoPDF(file);
return res;
}
let res = myfunc("some file");
console.log(res);
You need to be doing either this:
function convertFiletoPDF(file) {
return new Promise(function(resolve, reject) {
setTimeout(resolve, 400, "Done");
});
}
async function myfunc(file){
let res = await convertFiletoPDF(file);
return res;
}
(async() => {
try {
let res = await myfunc("some file");
console.log(res);
} catch (e) {
// Deal with the fact there was an error
}
})();
or with then and catch:
function convertFiletoPDF(file) {
return new Promise(function(resolve, reject) {
setTimeout(resolve, 400, "Done");
});
}
async function myfunc(file){
let res = await convertFiletoPDF(file);
return res;
}
myfunc("some file")
.then(res => {
console.log(res);
})
.catch(e => {
// Deal with the fact there was an error
});
convertFiletoPDF()
This function run and returned a Promise. This is fine.
myfunc()
Lets say myfunc takes 10 seconds. Javascript starts to wait newly created thread result from libuv via event loop mechanism. So, Javascript says, "That one is async, I will not wait, when it finishes it will let me know and i will run my then callback and then I will proceed with its output."
Javascript keeps his promise. Tries to run next below lines. myFunch is still working. Output is not ready yet. Returns undefined.
let res = myfunc(file);
console.log(res);
You get undefined.
Someone might find this example from my code useful. You can wrap it in a promise and then resolve the custom promise and then call another promise to confirm the receipt of the original web3 call.
return new Promise((resolve, reject) => {
tokenContract.methods.approve(
exchangeAddress,
BIG_NUMBER_1e50
)
.send({ from })
.once('transactionHash')
.once('receipt', receipt => resolve(receipt))
.on('confirmation')
.on('error', err => reject(err))
.then( receipt => // will be fired once the receipt its mined
console.log(receipt),
);
});

Categories

Resources