Promise reject is very slow on Node.JS <= 18 - node.js

Just run it in the Node.JS <= 18:
test.js
function delay(time) {
return new Promise((resolve) => {
setTimeout(resolve, time)
})
}
async function test() {
for (let i = 0; i < 1000000; i++) {
await new Promise((resolve, reject) => {
reject('value')
})
.then(() => {}, () => {})
// if (i % 10000 === 0) {
// await delay(100)
// }
}
console.log('OK')
const time0 = Date.now()
await delay(0)
console.log('Real delay time: ' + (Date.now() - time0))
}
test()
The test function will generate 1 million of Promise rejections. And after finish it will hang. It seems like the garbage collection called on idle, and it takes very long time.
If you replace the reject with the resolve, then everything will work quickly.
You can also uncomment the periodically delay, and it will work quickly. Unfortunately even this hack does not help on some browsers. Reject calls are gradually slowed down by dozens of times.
Does anyone know another way to bypass this problem?
PS: I'm writing a load test that checks many ways how a module works with Promise rejects. About 1.5 million variants. I can"t reduce the number of variants. I also plan to write many more similar tests in the future and I would like them to work

I found the solution:
function promiseRejected(reason?: any): PromiseLike<never> {
return {
then(_, reject): any {
void reject(reason)
},
}
}
export function rejectAsResolve(resolve: (value: any) => void, reason?: any) {
resolve(promiseRejected(reason))
}
and then use this instead reject:
new Promise((resolve) => {
rejectAsResolve(resolve, 'ERROR')
})

Related

How to properly write cloud functions that automatically update firestore documents

I am trying to write a firebase cloud function that runs a simple while loop every time a new user creates an account. For some reason, the update function only runs once and stops. The code i use is pasted below
const functions = require("firebase-functions");
const admin = require('firebase-admin');
admin.initializeApp();
const firestore = admin.firestore();
var data;
var counter = 0;
exports.onUserCreate = functions.firestore.document('testCollection/{docID}').onCreate(async(snapshot, context) =>{
data = snapshot.data();
while (counter < 5) {
setInterval(updateCounter(counter), 5000);
}
})
async function updateCounter(counter){
await firestore.collection('testCollection').doc(data['username']).update({
counter: admin.firestore.FieldValue.increment(1)
});
counter++;
}
Cloud Functions stops running your code when it hits the final } of your function, as otherwise it'd be billing your indefinitely for it.
If you want your code to continue running, you'll need to return a promise that resolves when your code is done with its work (up to 9 minutes).
exports.onUserCreate = functions.firestore.document('testCollection/{docID}').onCreate(async(snapshot, context) =>{
data = snapshot.data();
return Promise((resolve, reject) => {
while (counter < 5) {
setInterval(updateCounter(counter), 5000);
}
setInterval(() => {
if (counter >= 5) {
resolve()
}
}, 5000)
})
})
Note that the while (counter < 5) loop in the code still won't do what you expect it to do, but at least now the function will continue to run for a few moments and the counter will be incremented.
This is probably what you want:
exports.onUserCreate = functions.firestore.document('testCollection/{docID}').onCreate(async(snapshot, context) =>{
data = snapshot.data();
return Promise((resolve, reject) => {
setTimeout(updateCounter, 5000);
setTimeout(updateCounter, 10000);
setTimeout(updateCounter, 15000);
setTimeout(updateCounter, 20000);
setTimeout(updateCounter, 25000);
setInterval(() => {
if (counter >= 5) {
resolve()
}
}, 5000)
})
})
This calls updateCounter 5 times, each 5 seconds after the previous call. There is a chance that the final database update won't be completed before the call to resolve, so I strongly recommend learning more about asynchronous behavior by reading the documentation on sync, async, and promises and watching Doug's excellent promises and async behavior in Cloud Functions series.
if the requirement is to run this function 5 times, once every 5 seconds, this could work.
for (let i =0;i<5;i++){
await updateCounter();
await sleep (5000);
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}

Run parallel promises and discard rest when two of them resolved

I want to collect data from 10 different servers at the same time and when i got response 2 of them, i want to ignore/skip others. I solved this with promise.all() When i got 2 response, i call reject instead of resolve and do my job in catch(). But this seems a bit tricky to me, is there any better way to do this?
Here is something similar to the BlueBird Promise.some method suggested by #Neverever
function awaitSome(promises, count) {
if (!Array.isArray(promises) || promises.length < count) {
return Promise.reject();
}
return new Promise((resolve, reject) => {
const results = [];
const errors = [];
let completed = false;
promises.forEach((p) => {
p.then((result) => {
if (!completed) {
results.push(result);
if (results.length === count) {
completed = true;
resolve(results);
}
}
}).catch((err) => {
if (!completed) {
errors.push(err);
if (promises.length - errors.length < count) {
completed = true;
reject(errors);
}
}
});
});
});
}
And the usage whould be
awaitSome(
[
Promise.resolve(1),
Promise.reject(1),
Promise.resolve(1),
Promise.resolve(1)
],
2
)
.then((results) => {
console.log(results);
})
.catch((errors) => {
console.error(errors);
});
You should take a look at the BlueBird Promise.some
http://bluebirdjs.com/docs/api/promise.some.html
Given an Iterable(arrays are Iterable), or a promise of an Iterable, which produces promises (or a mix of promises and values), iterate over all the values in the Iterable into an array and return a promise that is fulfilled as soon as count promises are fulfilled in the array. The fulfillment value is an array with count values in the order they were fulfilled.
This example pings 4 nameservers, and logs the fastest 2 on console:
Promise.some([
ping("ns1.example.com"),
ping("ns2.example.com"),
ping("ns3.example.com"),
ping("ns4.example.com")
], 2).spread(function(first, second) {
console.log(first, second);
});
You can construct two Promises and put their resolves into an array. When one response comes back, pop() one of the resolves and call it. Repeat for the second response. Further responses can be ignored because the array is now empty.
Then, you can call Promise.all on the two constructed Promises, which will resolve when both resolves have been called.
const api = () => new Promise(res => {
const timeout = Math.random() * 3000;
setTimeout(() => {
console.log('resolving ' + timeout);
res(timeout);
}, timeout)
});
const resolves = [];
const prom1 = new Promise(resolve => resolves.push(resolve));
const prom2 = new Promise(resolve => resolves.push(resolve));
for (let i = 0; i < 10; i++) {
api().then(res => {
if (!resolves.length) return;
resolves.pop()(res);
});
}
Promise.all([prom1, prom2])
.then(([res1, res2]) => {
console.log('got 2 responses', res1, res2);
});
As you can see in the code above, although the latest 3rd to 10th API calls resolve, the Promise.all resolves as soon as the first and second quickest resolve.

Promise Jump Alwasy to The catch

I have an async function (a Promise) which does some things. I have to call it N times.
Every call represents a simulation point.
My first guess was to use a loop:
for(let i=0; i < N; i++) {
myAsyncFunc(data[i])
.then(() => myAsyncFunc(data[i]) )
}
Obviously, this does not work because the loops and before any subsequent call to myAsyncFun.
How can I call step-by-step the async function, waiting for results and proceed to the next step?
I tried whit this:
function myAsyncFunc(data) {
return new Promise( (resolve, reject) => {
anotherAsync.then(resolve).catch(reject);
}
}
function simulate(mode) {
[...Array(10)].reduce((p, _, i) =>
p.then(_ => new Promise(resolve => {
myAsyncFunc(data[i]); // <== this return a Promise
}
))
, Promise.resolve());
}
But the functions myAsyncFunc are not called in sequence.
I solved by using async/await which, apparently, seems to solve the acrobatics with asynchronous function calls details of JS
function myAsyncFunc(data) {
anotherAsync.then( () => return );
}
async function simulate(mode) {
for(let i=0; i < tempModel[0].linear.length; i++)
{
let a = await myAsyncFunc(mode,i);
}
}

async await with setInterval

function first(){
console.log('first')
}
function second(){
console.log('second')
}
let interval = async ()=>{
await setInterval(first,2000)
await setInterval(second,2000)
}
interval();
Imagine that I have this code above.
When I run it, first() and second() will be called at the same time; how do I call second() after first)() returns some data, for example, if first() is done, only then call second()?
Because first() in my code will be working with a big amount of data and if this 2 functions will be calling at the same time, it will be hard for the server.
How do I call second() each time when first() will return some data?
As mentioned above setInterval does not play well with promises if you do not stop it. In case you clear the interval you can use it like:
async function waitUntil(condition) {
return await new Promise(resolve => {
const interval = setInterval(() => {
if (condition) {
resolve('foo');
clearInterval(interval);
};
}, 1000);
});
}
Later you can use it like
const bar = waitUntil(someConditionHere)
You have a few problems:
Promises may only ever resolve once, setInterval() is meant to call the callback multiple times, Promises do not support this case well.
Neither setInterval(), nor the more appropriate setTimeout() return Promises, therefore, awaiting on them is pointless in this context.
You're looking for a function that returns a Promise which resolves after some times (using setTimeout(), probably, not setInterval()).
Luckily, creating such a function is rather trivial:
async function delay(ms) {
// return await for better async stack trace support in case of errors.
return await new Promise(resolve => setTimeout(resolve, ms));
}
With this new delay function, you can implement your desired flow:
function first(){
console.log('first')
}
function second(){
console.log('second')
}
let run = async ()=>{
await delay(2000);
first();
await delay(2000)
second();
}
run();
setInterval doesn't play well with promises because it triggers a callback multiple times, while promise resolves once.
It seems that it's setTimeout that fits the case. It should be promisified in order to be used with async..await:
async () => {
await new Promise(resolve => setTimeout(() => resolve(first()), 2000));
await new Promise(resolve => setTimeout(() => resolve(second()), 2000));
}
await expression causes async to pause until a Promise is settled
so you can directly get the promise's result without await
for me, I want to initiate Http request every 1s
let intervalid
async function testFunction() {
intervalid = setInterval(() => {
// I use axios like: axios.get('/user?ID=12345').then
new Promise(function(resolve, reject){
resolve('something')
}).then(res => {
if (condition) {
// do something
} else {
clearInterval(intervalid)
}
})
}, 1000)
}
// you can use this function like
testFunction()
// or stop the setInterval in any place by
clearInterval(intervalid)
You could use an IFFE. This way you could escape the issue of myInterval not accepting Promise as a return type.
There are cases where you need setInterval, because you want to call some function unknown amount of times with some interval in between.
When I faced this problem this turned out to be the most straight-forward solution for me. I hope it help someone :)
For me the use case was that I wanted to send logs to CloudWatch but try not to face the Throttle exception for sending more than 5 logs per second. So I needed to keep my logs and send them as a batch in an interval of 1 second. The solution I'm posting here is what I ended up using.
async function myAsyncFunc(): Promise<string> {
return new Promise<string>((resolve) => {
resolve("hello world");
});
}
function myInterval(): void {
setInterval(() => {
void (async () => {
await myAsyncFunc();
})();
}, 5_000);
}
// then call like so
myInterval();
Looked through all the answers but still didn't find the correct one that would work exactly how the OP is asked. This is what I used for the same purpose:
async function waitInterval(callback, ms) {
return new Promise(resolve => {
let iteration = 0;
const interval = setInterval(async () => {
if (await callback(iteration, interval)) {
resolve();
clearInterval(interval);
}
iteration++;
}, ms);
});
}
function first(i) {
console.log(`first: ${i}`);
// If the condition below is true the timer finishes
return i === 5;
}
function second(i) {
console.log(`second: ${i}`);
// If the condition below is true the timer finishes
return i === 5;
}
(async () => {
console.log('start');
await waitInterval(first, 1000);
await waitInterval(second, 1000);
console.log('finish');
})()
In my example, I also put interval iteration count and the timer itself, just in case the caller would need to do something with it. However, it's not necessary
In my case, I needed to iterate through a list of images, pausing in between each, and then a longer pause at the end before re-looping through.
I accomplished this by combining several techniques from above, calling my function recursively and awaiting a timeout.
If at any point another trigger changes my animationPaused:boolean, my recursive function will exit.
const loopThroughImages = async() => {
for (let i=0; i<numberOfImages; i++){
if (animationPaused) {
return;
}
this.updateImage(i);
await timeout(700);
}
await timeout(1000);
loopThroughImages();
}
loopThroughImages();
Async/await do not make the promises synchronous.
To my knowledge, it's just a different syntax for return Promise and .then().
Here i rewrote the async function and left both versions, so you can see what it really does and compare.
It's in fact a cascade of Promises.
// by the way no need for async there. the callback does not return a promise, so no need for await.
function waitInterval(callback, ms) {
return new Promise(resolve => {
let iteration = 0;
const interval = setInterval(async () => {
if (callback(iteration, interval)) {
resolve();
clearInterval(interval);
}
iteration++;
}, ms);
});
}
function first(i) {
console.log(`first: ${i}`);
// If the condition below is true the timer finishes
return i === 5;
}
function second(i) {
console.log(`second: ${i}`);
// If the condition below is true the timer finishes
return i === 5;
}
// async function with async/await, this code ...
(async () => {
console.log('start');
await waitInterval(first, 1000);
await waitInterval(second, 1000);
console.log('finish');
})() //... returns a pending Promise and ...
console.log('i do not wait');
// ... is kinda identical to this code.
// still asynchronous but return Promise statements with then cascade.
(() => {
console.log('start again');
return waitInterval(first, 1000).then(() => {
return waitInterval(second, 1000).then(() => {
console.log('finish again');
});
});
})(); // returns a pending Promise...
console.log('i do not wait either');
You can see the two async functions both execute at the same time.
So using promises around intervals here is not very useful, as it's still just intervals, and promises changes nothing, and make things confusing...
As the code is calling callbacks repeatedly into an interval, this is, i think, a cleaner way:
function first(i) {
console.log(`first: ${i}`);
// If the condition below is true the timer finishes
return i === 5;
}
function second(i) {
console.log(`second: ${i}`);
// If the condition below is true the timer finishes
return i === 5;
}
function executeThroughTime(...callbacks){
console.log('start');
let callbackIndex = 0; // to track current callback.
let timerIndex = 0; // index given to callbacks
let interval = setInterval(() =>{
if (callbacks[callbackIndex](timerIndex++)){ // callback return true when it finishes.
timerIndex = 0; // resets for next callback
if (++callbackIndex>=callbacks.length){ // if no next callback finish.
clearInterval(interval);
console.log('finish');
}
}
},1000)
}
executeThroughTime(first,second);
console.log('and i still do not wait ;)');
Also, this solution execute a callback every secondes.
if the callbacks are async requests that takes more than one sec to resolve, and i can't afford for them to overlap, then, instead of doing iterative call with repetitive interval, i would get the request resolution to call the next request (through a timer if i don't want to harass the server).
Here the "recursive" task is called lTask, does pretty much the same as before, except that, as i do not have an interval anymore, i need a new timer each iteration.
// slow internet request simulation. with a Promise, could be a callback.
function simulateAsync1(i) {
console.log(`first pending: ${i}`);
return new Promise((resolve) =>{
setTimeout(() => resolve('got that first big data'), Math.floor(Math.random()*1000)+ 1000);//simulate request that last between 1 and 2 sec.
}).then((result) =>{
console.log(`first solved: ${i} ->`, result);
return i==2;
});
}
// slow internet request simulation. with a Promise, could be a callback.
function simulateAsync2(i) {
console.log(`second pending: ${i}`);
return new Promise((resolve) =>{
setTimeout(() => resolve('got that second big data'), Math.floor(Math.random()*1000) + 1000);//simulate request that last between 1 and 2 sec.
}).then((result) =>{ // promise is resolved
console.log(`second solved: ${i} ->`,result);
return i==4; // return a promise
});
}
function executeThroughTime(...asyncCallbacks){
console.log('start');
let callbackIndex = 0;
let timerIndex = 0;
let lPreviousTime = Date.now();
let lTask = () => { // timeout callback.
asyncCallbacks[callbackIndex](timerIndex++).then((result) => { // the setTimeout for the next task is set when the promise is solved.
console.log('result',result)
if (result) { // current callback is done.
timerIndex = 0;
if (++callbackIndex>=asyncCallbacks.length){//are all callbacks done ?
console.log('finish');
return;// its over
}
}
console.log('time elapsed since previous call',Date.now() - lPreviousTime);
lPreviousTime = Date.now();
//console.log('"wait" 1 sec (but not realy)');
setTimeout(lTask,1000);//redo task after 1 sec.
//console.log('i do not wait');
});
}
lTask();// no need to set a timer for first call.
}
executeThroughTime(simulateAsync1,simulateAsync2);
console.log('i do not wait');
Next step would be to empty a fifo with the interval, and fill it with web request promises...

How to make a promise based function to be executed a few times with setTimeout before giving up in node 6 (i.e. no javascript specs for async/await)

I need to retrieve the actual value from a promise based function in a node 6 environment (Azure Functions), so I used co (https://www.npmjs.com/package/co) via generators (instead of the async/await paradigm) to handle the inner promise.
I need also to retry a few times that co/promise function using setTimeout before giving up definitively.
I am currently not able to make the following code work as expected. I am not sure where is the problem, but I can not "yield from the promise returned by co", so in the end the array that is passed around the recursive levels of the stack contains promises of values (1/0) rather than the actual values.
This is the wrapper for the "promise based function" that is handled with a try/catch to make sure we actually always return either 1 or 0.
const wannabeSyncFunc = () => {
console.log("outside co...");
return co(function *(){
console.log("inside co...");
try {
console.log("yielding...");
// promise that could be rejected hence try/catch
//
// I can not change this returned promise, so I must treat it
// as a promise that could potentially be rejected
let stuff = yield Promise.resolve();
console.log("stuff?", stuff);
console.log("returning 1");
return 1;
} catch (err) {
console.log("returning 0");
return 0;
}
console.log("after try/catch...");
});
}
This is the recursive/settimeout function that is supposed to try a few times before giving up.
const retryIntervalInMillis = 50;
const wannabeRecursiveFunc = (currTimes, attemptsArray) => {
return co(function *(){
console.log("Curr attemptsArray:", attemptsArray);
console.log("Curr attemptsArray[attemptsArray.length - 1]:", attemptsArray[attemptsArray.length - 1]);
console.log("Curr Promise.resolve(attemptsArray[attemptsArray.length - 1]):", Promise.resolve(attemptsArray[attemptsArray.length - 1]));
if (attemptsArray[attemptsArray.length - 1] == Promise.resolve(1)) {
console.log("Found the solution, returning straight away!")
return attemptsArray;
}
if (currTimes <= 0) {
console.log("Expired acquiring recursion");
return attemptsArray;
}
currTimes--;
const currValue = wannabeSyncFunc();
console.log(`First: currTimes: ${currTimes} currValue: ${currValue} curr attemptsArray: ${attemptsArray}`);
attemptsArray.push(currValue);
if (currValue === 1) {
return attemptsArray;
}
console.log(`Then: currTimes: ${currTimes} curr attemptsArray: ${attemptsArray}`);
return yield setTimeout(wannabeRecursiveFunc, currTimes*retryIntervalInMillis, currTimes, attemptsArray);
// return Promise.all(attemptsArray);
});
}
I've tried to invoke this in a few different ways like:
const numberOfAttempts = 3;
let theArray = wannabeRecursiveFunc(numberOfAttempts, []);
console.log(">>>", theArray);
Or assuming wannabeRecursiveFunc to return a promise and .then after the promise trying to print theArray.
I keep seeing inside the array these elements Promise { 1 } when printing it, but I would like to see either 1 or 0, so I hope those checks before the recursion could work as expected. At the moment those check don't work I think because I am comparing Promise { 1 } with 1.
However, I am not sure this is the reason why the whole thing is not working, and I am not even sure how to fix this. I am not sure whether co is needed (even in the node.js v6 environment), and how to make this promise/setTimeout work as expected.
I think I understand your objective: invoke a function that might fail, if it fails, wait a little bit and retry it. Do all of that with promises.
Here's a couple tools:
a promisified version of setTimeout...
function timeoutPromise(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
timeoutPromise(1000).then(() => {
console.log('time out expired');
});
A promise-returning dummy function that sometimes fails...
function fnThatMightFail() {
return new Promise((resolve, reject) => {
let fail = Math.random() < 0.40;
(fail)? reject('bad') : resolve('good');
});
}
fnThatMightFail().then(result => {
console.log(result);
}).catch(error => {
console.log(error);
});
And then, I think here's the recursive idea you're looking for. Pass in a function and a wait time between attempts, call recursively until we succeed...
function fnThatMightFail() {
return new Promise((resolve, reject) => {
let fail = Math.random() < 0.40;
(fail)? reject('bad') : resolve('good');
});
}
function timeoutPromise(ms) {
return new Promise((resolve) => {
setTimeout(() => resolve(), ms);
});
}
function fnRetryer(fn, tries, wait) {
if (tries <= 0) return Promise.reject('bad');
console.log('attempting fn');
return fn().then(result => {
console.log(`success: ${result}`);
return result;
}).catch(error => {
console.log(`error: ${error}, retrying after ${wait}ms`);
return timeoutPromise(wait).then(result => {
console.log(`${wait}ms elapsed, recursing...`);
return fnRetryer(fn, tries-1, wait);
});
});
}
fnRetryer(fnThatMightFail, 5, 1000).then(result => {
console.log(`we tried (and maybe tried) and got ${result}`);
}).catch(error => {
console.log('we failed after 5 tries, waiting 1s in between each try');
});
Note that you could add a parameter for a max number of attempts, decrement that on each recursive call and then don't recurse if that gets to zero. Also note, on the recursive call, you might opt to lengthen the wait time.

Resources