Determine which promise is slowest in Promises.all - javascript

I have been using Promise.all in my app.
For the purpose of improving app speed, how to determine which promise is the slowest?
const result = await Promise.all([
this.props.fetchUser(),
this.props.cacheResourcesAsync(),
this.props.initAmplitude(),
this.props.initAppVariables(),
]);

You can use a helper function for that:
async function time(p, name) {
const start = Date.now();
try {
return await p;
} finally {
const end = Date.now();
console.log(`${name} took ${end-start}ms`);
}
}
Then write
const result = await Promise.all([
time(this.props.fetchUser(), "user"),
time(this.props.cacheResourcesAsync(), "cacheResources"),
time(this.props.initAmplitude(), "amplitude"),
time(this.props.initAppVariables(), "appVars"),
]);

I'd do something like this :
let startTime = new Date();
Promise.all([
this.fetchUser().then(() => {
console.log('fetch user takes', new Date().getTime()-startTime.getTime());
return arguments;}),
this.fetchData().then(() => {
console.log('fetchData takes', new Date().getTime()-startTime.getTime());
return arguments;})
]);

Related

Use Promise.all to expedite the excution of one asynced and one synced tasks - async await syntax

let's say I got three functions :
Asynchronous function - asyncF() - for example , it calls a REST API.
Synchronous function sync()
And executer function exec() which invokes them both as fast as possible , preferably in parallel .
I want exec() to be written using the "Async-await" syntax, without using the "Callbacks" or "Promises" syntax .
Does it make sense to write it as :
async exec(){
const [res1, res2]= await Promise.all([asyncF(), sync()])
...
}
Is there a better way?
const p = asyncF();
const syncResult = sync();
const asyncResult = await p;
Start the async task, then run the sync function, then wait for the promise to resolve.
If the sync task takes longer, there would be no extra wait async one, as the promise will have resolved:
function sync() {
const end = new Date();
end.setSeconds(end.getSeconds() + 3);
//loop for 3 seconds
for (let now = new Date(); now < end; now = new Date());
return 2;
}
function asyncF() {
//after 1 seconds return the value 40
return new Promise(resolve => setTimeout(resolve, 1000, 40));
}
async function exec(){
const p = asyncF();
const syncResult = sync();
const asyncResult = await p;
return syncResult + asyncResult;
}
const start = new Date();
exec()
.then(result => console.log(`result after ${new Date() - start}ms was: ${result}`));
If the async task takes longer, then there is an extra wait for the promise to resolve:
function sync() {
//return immediately
return 2;
}
function asyncF() {
//after 3 seconds return the value 40
return new Promise(resolve => setTimeout(resolve, 3000, 40));
}
async function exec(){
const p = asyncF();
const syncResult = sync();
const asyncResult = await p;
return syncResult + asyncResult;
}
const start = new Date();
exec()
.then(result => console.log(`result after ${new Date() - start}ms was: ${result}`))
This assumes, of course, that the async task is properly asynchronous. An async function will still be executed synchronously until it reaches an await or ends.
On errors
Do note that async rejections should still be handled if that is a risk. The problem here is that if sync() fails, then the await p would never be reached, thus a code like
const p = asyncF();
try {
const syncResult = sync();
const asyncResult = await p;
return syncResult + asyncResult;
} catch (error) { /* do something */}
will not properly handle the rejection coming from p.
For this we can take apage from Promise.allSettled() and safely wrap the promise results into an object with a status that tells whether it was fulfilled or rejected:
const p = asyncF()
.then(value => ({ status: "fulfilled", value }))
.catch(reason => ({ status: "rejected", reason });
This can further be encapsulated into small helpers:
const wrapPromise = p =>
p.then(
value => ({ status: "fulfilled", value }),
reason => ({ status: "rejected", reason })
);
const unwrapPromise = p =>
p.then((result) => {
const {status, value, reason} = result;
if (status === "fulfilled")
return value;
else if (status === "rejected")
throw reason;
throw new Error(`Unknown status ${status}, the value of the promise was ${result}`);
});
Using const bar = wrapPromise(foo) would prevent unhandled promise rejections by handling them and await unwrapPromise(bar) will then trigger the failure to happen at that point when it can be handled:
const p = wrapPromise(asyncF());
try {
const syncResult = sync();
const asyncResult = await unwrapPromise(p);
return syncResult + asyncResult;
} catch(error) { /* do something */ }
Thus if sync() fails first, there is no extra unhandled promise rejection going to happen later:
const wrapPromise = p =>
p.then(
value => ({ status: "fulfilled", value }),
reason => ({ status: "rejected", reason })
);
const unwrapPromise = p =>
p.then((result) => {
const {status, value, reason} = result;
if (status === "fulfilled")
return value;
else if (status === "rejected")
throw reason;
throw new Error(`Unknown status ${status}, the value of the promise was ${result}`);
});
function sync() {
const end = new Date();
end.setSeconds(end.getSeconds() + 1);
//loop for 1 seconds
for (let now = new Date(); now < end; now = new Date());
throw new Error("sync() failed");
}
function asyncF() {
//after 3 seconds reject with error the value 40
return new Promise((_, reject) => setTimeout(reject, 3000, new Error("asyncF() failed")));
}
async function exec(){
const p = wrapPromise(asyncF());
try {
const syncResult = sync();
const asyncResult = await unwrapPromise(p);
return syncResult + asyncResult;
} catch(error) {
throw new Error(`exec() failed because: ${error.message}`);
}
}
const start = new Date();
exec()
.then(result => console.log(`result after ${new Date() - start}ms was: ${result}`))
.catch(error => console.error(`error after ${new Date() - start}ms was: ${error.message}`));
While if sync() succeeds after asyncF() has failed, that is still OK, since the await unwrapPromise(p) will be the point where an exception will be raised:
const wrapPromise = p =>
p.then(
value => ({ status: "fulfilled", value }),
reason => ({ status: "rejected", reason })
);
const unwrapPromise = p =>
p.then((result) => {
const {status, value, reason} = result;
if (status === "fulfilled")
return value;
else if (status === "rejected")
throw reason;
throw new Error(`Unknown status ${status}, the value of the promise was ${result}`);
});
function sync() {
const end = new Date();
end.setSeconds(end.getSeconds() + 3);
//loop for 3 seconds
for (let now = new Date(); now < end; now = new Date());
return 2;
}
function asyncF() {
//after 1 seconds reject with error the value 40
return new Promise((_, reject) => setTimeout(reject, 1000, new Error("asyncF() failed")));
}
async function exec(){
const p = wrapPromise(asyncF());
try {
const syncResult = sync();
const asyncResult = await unwrapPromise(p);
return syncResult + asyncResult;
} catch(error) {
throw new Error(`exec() failed because: ${error.message}`);
}
}
const start = new Date();
exec()
.then(result => console.log(`result after ${new Date() - start}ms was: ${result}`))
.catch(error => console.error(`error after ${new Date() - start}ms was: ${error.message}`));
The async failure can be handled more simply if a fallback value is fine:
const p = asyncF().catch(error => {
//optionally do something with error
return -1; //fallback value
});
Then the await p will simply produce -1 on a failure.
You can't really run two things in parallel
And it also depends on how your async function work, here async function will block an entire thread for almost a second:
exec()
async function exec() {
const startTime = Date.now()
const [res1, res2] = await Promise.all([asyncF(), sync()])
console.log('finished', Date.now() - startTime, 'ms')
}
function asyncF() {
console.log('start async')
return Promise.resolve().then(() => {
let i = 100000000;
let x = 0
while (i--) {
x += Math.random()
}
return 'async'
})
}
function sync() {
console.log('start sync')
return 'sync'
}
About the question in the comment:
What Promise.all really do here is replace a map function
exec()
async function exec() {
const values = await Promise.all([sync(), sync(), sync()])
console.log(values)
const values2 = [sync, sync, sync].map(fn => fn())
console.log(values2)
}
function sync() {
return Math.random()
}
Is it better? No
Is it faster? Also no

Proper way to decorate async function [duplicate]

This question already has answers here:
How to measure the execution time of a promise?
(5 answers)
Closed 1 year ago.
I want to make a function decorator and measure async's function execution time, and return resolve or reject results as indented without decorating. Any clue how to achieve this? Most guides are about sync functions and are not touching the async world.
This is an example to illustrate the desired functionality
async exampleFunction () {
try{
const response = await aPromise() // The goals is to measure this one with a decorator measure(aPromise) and dispatch actions if the fetching takes a long time
}
catch (err){
// Do something with error
}
Here's a higher-order function that can measure the time it takes for the returned promise to settle either by fulfilling or rejecting:
function measure(fn) {
return async (...args) => {
const begin = performance.now();
try {
return await fn(...args);
} finally {
const end = performance.now();
console.log(`Execution time: ${end - begin}`);
}
};
}
Example usage:
exampleFunction();
async function exampleFunction() {
try {
const value = await measure(defer)('foo', 1000);
// where normally you'd write:
// const value = await defer('foo', 1000);
console.log(`Resolved with ${value}`);
} catch (reason) {
console.log(`Rejected with ${reason}`);
}
}
function measure(fn) {
return async (...args) => {
const begin = performance.now();
try {
return await fn(...args);
} finally {
const end = performance.now();
console.log(`Execution time: ${end - begin}`);
}
};
}
function defer(result, ms) {
return new Promise((resolve, reject) => {
setTimeout(() => {
const settle = Math.random() < 0.5 ? resolve : reject;
settle(result);
}, ms);
});
}
Something like this should do the trick:
export function MeasureExecutionTime(target, propertyKey, descriptor) {
const fun = descriptor.value;
descriptor.value = async function(...args) {
const start = performance.now();
const result = await fun.apply(target, args);
const end = performance.now();
console.log(`Execution time: ${end - start}`);
return result;
}
}

Why using await is also giving a pending promise in my code?

Why I am getting pending promises even after using await in the getCart() method? Is there something conceptually wrong in my code?
Please help. Thank you!
class User {
constructor(..., cart) {
//...
this.cart = cart; //{items: []}
}
getCart() {
return (async () => {
const products = this.cart.items.map(async (item) => {
const product = await Products.getProductDetail(item.productId); //this returns a promise.
product.qty = item.qty;
return product; //<pending> Promise
});
console.log(products); //<pending> Promise
return products;
})();
}
}
Here is the function call:
exports.renderCart = (req, res, next) => {
(async () => {
const products = await req.user.getCart(); //req.user is a User class object, ignore it.
console.log(products); //pending promise :(
res.render('shop/cart', { products, pageTitle: 'Cart'});
})();
};
products in your code is an array of promises. You need to wait for them all to finish using Promise.all.
The problem can be demonstrated with a bit of a mockup
var fakeWait = x => new Promise(resolve => setTimeout(() => resolve(x), 1000));
function test(){
const items = [1,2,3,4,5]
const result = items.map( async x => await fakeWait(x));
console.log(result); // list of unresolved promises
}
async function test2(){
const items = [1,2,3,4,5]
const result = await Promise.all(items.map( async x => await fakeWait(x)));
console.log(result); // list of resolved values
}
test();
test2();
In addition, your getCart method is overly complex - it does not need to be an IIFE
getCart() {
const products = this.cart.items.map(async (item) => {
const product = await Products.getProductDetail(item.productId); //this returns a promise.
product.qty = item.qty;
return product; //<pending> Promise
});
return Promise.all(products);
}
And then simply:
const products = await req.user.getCart();

how do I create a nameless async function - Nodejs

I was wondering if it's possible to create a nameless function with the quick functional notation on javascript. What I mean by changing this:
var lobby = ((io) => {
...
}
return {
getWhiteBoardId: (name2, time2, role2, pass2, need_id) => {
let promise = new Promise((res, rej) => {
setTimeout(() => res("Now it's done!"), 1000)
});
// wait until the promise returns us a value
let result = await promise;
}
})(io);
I then want to later be able to call this function:
whiteboardId = lobby.getWhiteBoardId(req.body.name, req.body.time, req.body.role, req.body.pass, need_id);
to have something at the beginning such as this:
var lobby = (async (io) => {
so that I can call my Promise and await
// defining io, to execute the code, you not need to write the next line
const io = {};
const lobby = ((io) => {
// some pre-existing code
return {
// you are using await in this method so add async to the method signature
getWhiteBoardId: async (...args) => {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve(args), 1000)
});
// wait until the promise returns us a value
let result = await promise;
// ... some other code/logic as needed
// i have assumed you need to return the result
return result;
}
}
})(io);
// TEST: calling getWhiteBoardID method
lobby.getWhiteBoardId("Hi", "There").then(console.log);
// or
(async () => {
const res = await lobby.getWhiteBoardId("Hello World")
console.log(res);
})();
console.log("First!");

await for indexdb event in async function

I'm trying to return a custom object from a async function that works as wrapper for a put using indexdb.
Using Promises this is easy.
However, using async/await became more challenging...
const set = async (storeName, key, value) => {
if (!db)
throw new Error("no db!");
try {
const result = {};
let tx = db.transaction(storeName, "readwrite");
let store = tx.objectStore(storeName);
let r = store.put({ data: key, value: value });
console.log(r);
r.onsuccess = async () => {
console.log('onsuccess');
result.something = true;
}
r.onerror = async () => {
console.log('onerror');
result.something = false;
}
await r.transaction.complete; // ok... this don't work
// how can I await until onsuccess or onerror runs?
return result;
} catch (error) {
console.log(error);
}
}
The ideia is to return a composed object... however all my attemps fails as onsuccess runs after returning the result.
I googled a lot and could't find a way to proper await for onsuccess/onerror events.
I know that returning a Promise is more easy as resolve(result) would end returning what I want... but i'm trying to learn to make same code using async/await.
Thank you so much,
Try this:
function set(db, storeName, key, value) {
return new Promise((resolve, reject) => {
let result;
const tx = db.transaction(storeName, 'readwrite');
tx.oncomplete = _ => resolve(result);
tx.onerror = event => reject(event.target.error);
const store = tx.objectStore(storeName);
const request = store.put({data: key, value: value});
request.onsuccess = _ => result = request.result;
});
}
async function callIt() {
const db = ...;
const result = await set(db, storeName, key, value);
console.log(result);
}
Edit, since you insist on using the async qualifier for the set function, you can do this instead. Please note I find this pretty silly:
async function set(db, storeName, key, value) {
// Wrap the code that uses indexedDB in a promise because that is
// the only way to use indexedDB together with promises and
// async/await syntax. Note this syntax is much less preferred than
// using the promise-returning function pattern I used in the previous
// section of this answer.
const promise = new Promise((resolve, reject) => {
let result;
const tx = db.transaction(storeName, 'readwrite');
tx.oncomplete = _ => resolve(result);
tx.onerror = event => reject(event.target.error);
const store = tx.objectStore(storeName);
const request = store.put({data: key, value: value});
request.onsuccess = _ => result = request.result;
});
// We have executed the promise, but have not awaited it yet. So now we
// await it. We can use try/catch here too, if we want, because the
// await will translate the promise rejection into an exception. Of course,
// this is also rather silly because we are doing the same thing as just
// allowing an uncaught exception to exit the function early.
let result;
try {
result = await promise;
} catch(error) {
console.log(error);
return;
}
// Now do something with the result
console.debug('The result is', result);
}
Ultimately you'll end up wrapping IDB in a promise-friend library, but for your specific need, you could use something like this:
function promiseForTransaction(tx) {
return new Promise((resolve, reject) => {
tx.oncomplete = e => resolve();
tx.onabort = e => reject(tx.error);
});
}
And then in your code you can write things such as:
await promiseForTransaction(r.tx);
... which will wait until the transaction completes, and throw an exception if it aborts. (Note that this requires calling the helper
before the transaction could possibly have completed/aborted, since
it won't ever resolve if the events have already fired)
I can't confirm it right now but I think it should be await tx.complete instead of await r.transaction.complete;.
But a general solution that would work even if the API would not support Promises directly would be to wrap a new Promise around the onsuccess and onerror and use await to wait for that Promise to resolve, and in your onsuccess and onerror you then call the resolve function:
const set = async (storeName, key, value) => {
if (!db)
throw new Error("no db!");
try {
const result = {};
let tx = db.transaction(storeName, "readwrite");
let store = tx.objectStore(storeName);
let r = store.put({
data: key,
value: value
});
console.log(r);
await new Promise((resolve, reject) => {
r.onsuccess = () => {
console.log('onsuccess');
result.something = true;
resolve()
}
r.onerror = () => {
console.log('onerror');
result.something = false;
// I assume you want to resolve the promise even if you get an error
resolve()
}
})
return result;
} catch (error) {
console.log(error);
}
}
I would furhter change it to:
try {
await new Promise((resolve, reject) => {
r.onsuccess = resolve
r.onerror = reject
})
console.log('success');
result.something = true;
} catch(err) {
console.log('error');
result.something = false;
}

Categories

Resources