I've tried to implement delays in Promises this way:
function postAndSetContentfulId (client, code) {
return delay().then(_ => {
return postToContentful(client, code).then(setCode)
})
}
function delay () {
let multipler = Math.floor((count++) / 100) + 1
let milliseconds = multipler * 2000
return new Promise(resolve => setTimeout(() => resolve(), milliseconds))
}
I need to delay an third party API call in order to avoid rate limiting issues. This seems not to be actually working as I get all executed in a bunch.
I've got a simple rate limited promise queue I wrote years ago, which has served me very well
const rateLimited = perSecond => {
const milliSeconds = Math.floor(1000 / perSecond);
let promise = Promise.resolve(Date.now());
return fn => promise.then(lastRun => {
const wait = Math.max(0, milliSeconds + lastRun - Date.now());
promise = promise.then(() => new Promise(resolve => setTimeout(resolve, wait))).then(() => Date.now());
return promise.then(() => fn());
});
};
const example = rateLimited(2); // 2 per second for the example
let allPromises = Array.from({length:10}, (_, i) => i)
.map(v => example(() => Promise.resolve(v)));
console.time('Elapsed');
console.log('Started - should take 5 seconds');
Promise.all(allPromises).then(results => {
console.log(results);
console.timeEnd('Elapsed');
});
For your code, you would use rateLimited like
const q = rateLimited(20); // 20 per second limit for example
function postAndSetContentfulId(client, code) {
return q(() => postToContentful(client, code).then(setCode));
}
Related
I need to re-execute a hand made for loop (because I need time between each step) 10s after it ends forever.
So after few tries, I came with this code, but it doesn't work because it re-executes every 10s, not after the loop finishes. I tried to but async function in the interval and put all my code in an await one, but it didn't work.
async function refreshEDT() {
let rows = appModel.getAll()
let orderFiliere = {};
await rows.then((res) => {
for (let i = 0; i < res.length; i++) {
if (res[i].filiere in orderFiliere) {
orderFiliere[res[i].filiere].push(res[i])
} else {
orderFiliere[res[i].filiere] = [res[i]]
}
}
})
return orderFiliere
}
let edt = refreshEDT()
setInterval(() => {
edt = refreshEDT()
}, 1000 * 60 * 60) //ms to s to m to h
setInterval(() => {
edt.then((lessons) => {
(function loop(i) {
let lessons_key = Object.keys(lessons)
setTimeout(() => {
// processing things
if (--i) loop(i);
}, 1000 * 10) //ms to s to 10s
})(Object.keys(lessons).length - 1)
})
console.log(1)
}, 1000 * 10) //ms to s to 10s
Do you have any solution?
Ty!
EDIT
Let's explain what I try to do:
I get things on my DB, try to classify by "filiere", one of my column, and return that object.
Those data are reloaded every hour.
After that, I need to emit with socket.io every "filiere" with 10s between, and repeat that.
Perhaps the code will be easier to read when you wrap setTimeout with a promise to await.
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
async function refreshEDT() {
const res = await appModel.getAll();
const orderFiliere = res.reduce((acc, value) => {
const { filiere } = value;
if (filiere in acc) {
acc[filiere].push(value);
} else {
acc[filiere] = [value];
}
return acc;
}, {});
return orderFiliere;
}
// consider naming this
(async () => {
let edt = refreshEDT();
setInterval(() => {
edt = refreshEDT();
}, 1000 * 60 * 60);
while (true) {
const lessons = await edt;
for (const lessons_key of Object.keys(lessons)) {
await sleep(1000 * 10);
// processing things
}
// repeat after 10 seconds
await sleep(1000 * 10);
}
})();
With ES2022, you can use Array.prototype.groupBy() to simplify your refreshEDT() implementation:
async function refreshEDT() {
const res = await appModel.getAll();
const orderFiliere = res.groupBy(({ filiere }) => filiere);
return orderFiliere;
}
You could try using setTimeout to call your function again.
const myFunc = () => {
// processing things
setTimeout(myFunc , 10000)
}
myFunc();
I think this could help if i understood the problem correctly :
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
async yourFunction() {
const secondsToWait: number = 10;
let i = 0;
for (let item of List) {
// Do stuff
if (i === list.length -1) {
await wait(secondsToWait*1000);
yourFunction();
}
i++;
}
}
Basically I have two async functions. One of them is a simple 5 second timeout and the other is a complicated async function with multiple steps.
Here is an example
const delay = ms => new Promise(res => setTimeout(res, ms));
class Runner {
async start() {
let printStuff = async () => {
for(let i = 0 ; i < 50; i++){
console.log(i);
await delay(50);
}
}
let printLetters = new Promise(async function(resolve, reject) {
const length = Math.floor(Math.random() * 10)
//dont know how long this will take
for(let i = 0; i < length; i++){
await printStuff();
}
resolve('letters');
});
let timeout = new Promise(async function(resolve, reject) {
await delay(5000);
resolve('timeout')
});
const finished = await Promise.all([timeout, printLetters]);
if(finished === 'timeout'){
this.stop();
}
}
stop(){
//stop printing stuff instantly
}
}
const myRunner = new Runner();
myRunner.start();
//could call myRunner.stop(); if the user canceled it
The way I would implement this would add a global variable and include an if statement inside the for loop to check if the interrupt has been called but I am wondering if there is a better way to implement this. An issue with this solution would be it would print a few more numbers. I would have to add another check to the other for loop and this could get messy quickly.
Here is a simple demo that uses my own library.
import { CPromise } from "c-promise2";
const task = CPromise.promisify(function* () {
let printStuff = CPromise.promisify(function* () {
for (let i = 0; i < 10; i++) {
console.log(i);
yield CPromise.delay(100);
}
});
const length = Math.floor(Math.random() * 10) + 3;
//dont know how long this will take
for (let i = 0; i < length; i++) {
yield printStuff();
}
return "letters";
});
const promise = task()
.timeout(5000)
.then(
(result) => console.log(`Done: ${result}`),
(err) => console.warn(`Fail: ${err}`)
);
setTimeout(() => {
promise.cancel();
}, 2000);
Is this what you're after?
What changed:
Promise.all replaced with Promise.race
added isStopped prop which makes the "complicated async function with multiple steps" skip execution for the remaining steps. it doesn't kill it, though. Promises are not cancelable.
const delay = ms => new Promise(res => setTimeout(res, ms));
class Runner {
isStopped = false;
async start() {
const printStuff = async () => {
let i = 0;
while (!this.isStopped) {
console.log(i++);
await delay(50);
}
}
const printLetters = new Promise(
resolve => printStuff()
.then(() => resolve('letters'))
);
const timeout = new Promise(
resolve => delay(5000)
.then(() => resolve('timeout'))
);
const finished = await Promise.race([timeout, printLetters]);
console.log({ finished });
if (finished === 'timeout') {
this.stop();
}
}
stop() {
this.isStopped = true;
}
}
const myRunner = new Runner();
myRunner.start();
<button onclick="myRunner.stop()">stop</button>
Initial answer (left it in as the comments reference it, not what's above; and in case someone finds it useful in 2074):
Here's an example outlining what I was suggesting in the comment. run() below returns a race between a rejector happening after 1s and a fulfiller which resolves in random time between 0 and 2s.
const rejector = (timeout) => new Promise((resolve, reject) => {
setTimeout(reject, timeout, 'rejected')
});
class Runner {
run() {
return Promise.race([
rejector(1000),
new Promise((resolve, reject) => {
setTimeout(resolve, Math.random() * 2000, 'fulfilled')
})
])
}
}
const t0 = performance.now();
[...Array(6).fill()].forEach((_, key) => {
const runner = new Runner();
runner.run()
.then(r => console.log(`Proomise ${key} ${r} after ${performance.now() - t0}ms`))
.catch(err => console.log(`Promise ${key} ${err} after ${performance.now() - t0}ms`));
})
Note: initially I placed the rejector inside the class but (at least for the above example) I don't see why it should not stay outside (in a real case scenario, imported from a helper file).
If you require an instantaneous stop capability, you would probably want to execute the print job as a external script. Then you use child processes like this.
const { spawn } = require('child_process');
class Runner {
......
start() {
this.job[somejobId] = spawn('command to execute script');
//this can be anything, including a node script, e.g. `node start.js`
.....
}
stop(jobId) {
if (jobId) {
//this will kill the script that you spawn above
this.job[jobId].kill('SIGHUP');
}
}
stopAllJobs() {
// looping through the job queue to kill all the jobs
this.job.forEach(job => job.kill('SIGHUP'))
}
}
You will have more info on how to start a child process from the node doc website https://nodejs.org/api/child_process.html#subprocesskillsignal
If your job (external script) is stalling, it's recommended that you only use the above codes if you have a minimum 2 CPU core, else it will affect your main process if your script is heavy.
I have a few hundreds of things to render in parallel in an html5 canvas. These are drawn in parallel in a Promise.all call. Now, I would like to know which of these promise is the last to be resolved.
// get a promise that will resolve in between 0 and 5 seconds.
function resolveAfterSomeTime(): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, Math.random() * 5000));
}
const myPromises = [];
for (let i = 0; i < 100; i++) {
myPromises.push(resolveAfterSomeTime);
}
Promise.all(myPromises).then(() => {
// find out which promise was the last to resolve.
})
In my case, I have multiple classes with each a render() function. Some of these are heavier than others, but I want to know which ones.
I have something along these lines, and I would like to know which promise is the slowest to resolve, so that I can optimise it.
The best way I can think of is to use a counter indicating the number of promises that have resolved so far:
function resolveAfterSomeTime() {
return new Promise((resolve) => setTimeout(resolve, Math.random() * 5000));
}
const myPromises = [];
let resolveCount = 0;
for (let i = 0; i < 100; i++) {
myPromises.push(
resolveAfterSomeTime()
.then(() => {
resolveCount++;
if (resolveCount === 100) {
console.log('all resolved');
console.log('array item', i, 'took longest');
}
})
);
}
Here's a way where each promise sets the value of lastPromiseToResolve after resolving. The last promise to resolve would set it last.
// get a promise that will resolve in between 0 and 5 seconds.
function resolveAfterSomeTime(): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, Math.random() * 5000));
}
let lastPromiseToResolve = null
const myPromises = [];
for (let i = 0; i < 100; i++) {
const promise = resolveAfterSomeTime()
myPromises.push(promise.then(() => {
lastPromiseToResolve = promise // all the promises will set lastPromiseToResolve
}));
}
Promise.all(myPromises).then(() => {
console.log(lastPromiseToResolve) // this would be the last promise to resolve
})
You could time each promise. You could even assign an identifier to each one if you want to know specifically which is resolving. The timePromise function below takes an id and a function that returns a promise, times that promise, and logs the result. It doesn't change the result of the promises, so you can use myPromises as you normally would.
function resolveAfterSomeTime() {
return new Promise((resolve) => setTimeout(resolve, Math.random() * 1000));
}
// f is a function that returns a promise
function timePromise(id, f) {
const start = Date.now()
return f()
.then(x => {
const stop = Date.now()
console.log({id, start, stop, duration: (stop - start)})
return x
})
}
const myPromises = [];
for (let i = 0; i < 100; i++) {
myPromises.push(timePromise(i, resolveAfterSomeTime));
}
Promise.all(myPromises).then(() => {
// find out which promise was the last to resolve.
})
I'm not sure how you're creating your array of promises in your actual code, so it might not be straightforward to wrap each promise in a function that returns it. But you could likely adapt this to work with your situation.
If you aren't concerned with knowing exactly how long each takes, you could just have timePromise take a promise that's already started, and time from when timePromise is called to when it resolves. This wouldn't be as accurate, but would still give you a general idea, especially if one or a few promises are taking much longer than others.
Something like this:
function timePromise(id, p) {
const start = Date.now()
return p
.then(x => {
const stop = Date.now()
console.log({id, start, stop, duration: (stop - start)})
return x
})
}
const myPromises = [];
for (let i = 0; i < 100; i++) {
myPromises.push(timePromise(i, resolveAfterSomeTime()));
}
I'm trying to get the execution/response time of an asynchronous function that executes inside a node-fetch operation, like the following
async function getEditedData() {
var a = await fetch(`https://api.example.com/resorce_example`);
var b = await a.json();
// Some aditional treatment of the object obtained (b)
console.log("End of the asynchronous function")
}
I Used the library perf_hooks like this, but the execution time shows before
const hrtime = require ('perf_hooks').performance.now ;
var start = hrtime ();
getEditedData();
var end = hrtime ();
console.log (end - start);
I found the async_hooks library https://nodejs.org/api/perf_hooks.html#perf_hooks_measuring_the_duration_of_async_operations , but I canĀ“t understand how it works. I am a basic in javascript/nodejs
You could simply store Date.now() in some variable and then check Date.now() when your Promise resolves (or rejects) and subtract to find the difference. For example:
const simulateSomeAsyncFunction = new Promise((resolve, reject) => {
console.log('Initiating some async process, please wait...')
const startTime = Date.now();
setTimeout(() => {
resolve(Date.now() - startTime);
}, 3000);
});
simulateSomeAsyncFunction.then(msElapsed => {
console.log(`Async function took ${msElapsed / 1000} seconds to complete.`);
});
Note: You could write code that achieves the same thing and appears to be synchronous by using await/async since that is just "syntactic sugar" built on top of Promises. For example:
const simulateSomeAsyncFunction = () => {
console.log('Initiating some async process, please wait...');
return new Promise((resolve, reject) => {
setTimeout(resolve, 3000);
});
};
// Await is required to be called within an async function so we have to wrap the calling code in an async IIFE
(async() => {
const startTime = Date.now();
await simulateSomeAsyncFunction();
const msElapsed = Date.now() - startTime;
console.log(`Async function took ${msElapsed / 1000} seconds to complete.`);
})();
Starting with a simple async function -
const fakeAsync = async (value) => {
const delay = 2000 + Math.random() * 3000 // 2 - 5 seconds
return new Promise(r => setTimeout(r, delay, value))
}
fakeAsync("foo").then(console.log)
console.log("please wait...")
// "please wait..."
// "foo"
We could write a generic function, timeit. This is a higher-order function that accepts a function as input and returns a new function as output. The new function operates like a decorated version of the original -
const timeit = (func = identity) => async (...args) => {
const t = Date.now()
const result = await func(...args)
return { duration: Date.now() - t, result }
}
// decorate the original
const timedFakeAsync = timeit(fakeAsync)
// call the decorated function
timedFakeAsync("hello").then(console.log)
timedFakeAsync("earth").then(console.log)
// { duration: 3614, result: "earth" }
// { duration: 4757, result: "hello" }
The timed version of our function returns an object, { duration, result }, that reports the runtime of our async function and the result.
Expand the snippet below to verify the results in your own browser -
const identity = x =>
x
const timeit = (func = identity) => async (...args) => {
const t = Date.now()
const result = await func(...args)
return { duration: Date.now() - t, result }
}
const fakeAsync = async (value) => {
const delay = 2000 + Math.random() * 3000 // 2 - 5 seconds
return new Promise(r => setTimeout(r, delay, value))
}
const timedFakeAsync = timeit(fakeAsync)
timedFakeAsync("hello").then(console.log)
timedFakeAsync("earth").then(console.log)
console.log("please wait...")
// "please wait..."
// { duration: 3614, result: "earth" }
// { duration: 4757, result: "hello" }
If you expect to set the end after getEditedData() is completed, you actually need to await getEditedData(). Otherwise, you'll go right past it while it executes... asynchrnously.
I have several promises that I need to resolve before going further.
Promise.all(promises).then((results) => {
// going further
});
Is there any way I can have the progress of the Promise.all promise?
From the doc, it appears that it is not possible. And this question doesn't answer it either.
So:
Don't you agree that this would be useful? Shouldn't we query for this feature?
How can one implement it manually for now?
I've knocked up a little helper function that you can re-use.
Basically pass your promises as normal, and provide a callback to do what you want with the progress..
function allProgress(proms, progress_cb) {
let d = 0;
progress_cb(0);
for (const p of proms) {
p.then(()=> {
d ++;
progress_cb( (d * 100) / proms.length );
});
}
return Promise.all(proms);
}
function test(ms) {
return new Promise((resolve) => {
setTimeout(() => {
console.log(`Waited ${ms}`);
resolve();
}, ms);
});
}
allProgress([test(1000), test(3000), test(2000), test(3500)],
(p) => {
console.log(`% Done = ${p.toFixed(2)}`);
});
You can add a .then() to each promise to count whos finished.
something like :
var count = 0;
var p1 = new Promise((resolve, reject) => {
setTimeout(resolve, 5000, 'boo');
});
var p2 = new Promise((resolve, reject) => {
setTimeout(resolve, 7000, 'yoo');
});
var p3 = new Promise((resolve, reject) => {
setTimeout(resolve, 3000, 'foo');
});
var promiseArray = [
p1.then(function(val) {
progress(++count);
return val
}),
p2.then(function(val) {
progress(++count);
return val
}),
p3.then(function(val) {
progress(++count);
return val
})
]
function progress(count) {
console.log(count / promiseArray.length);
}
Promise.all(promiseArray).then(values => {
console.log(values);
});
This has a few advantages over Keith's answer:
The onprogress() callback is never invoked synchronously. This ensures that the callback can depend on code which is run synchronously after the call to Promise.progress(...).
The promise chain propagates errors thrown in progress events to the caller rather than allowing uncaught promise rejections. This ensures that with robust error handling, the caller is able to prevent the application from entering an unknown state or crashing.
The callback receives a ProgressEvent instead of a percentage. This eases the difficulty of handling 0 / 0 progress events by avoiding the quotient NaN.
Promise.progress = async function progress (iterable, onprogress) {
// consume iterable synchronously and convert to array of promises
const promises = Array.from(iterable).map(this.resolve, this);
let resolved = 0;
// helper function for emitting progress events
const progress = increment => this.resolve(
onprogress(
new ProgressEvent('progress', {
total: promises.length,
loaded: resolved += increment
})
)
);
// lift all progress events off the stack
await this.resolve();
// emit 0 progress event
await progress(0);
// emit a progress event each time a promise resolves
return this.all(
promises.map(
promise => promise.finally(
() => progress(1)
)
})
);
};
Note that ProgressEvent has limited support. If this coverage doesn't meet your requirements, you can easily polyfill this:
class ProgressEvent extends Event {
constructor (type, { loaded = 0, total = 0, lengthComputable = (total > 0) } = {}) {
super(type);
this.lengthComputable = lengthComputable;
this.loaded = loaded;
this.total = total;
}
}
#Keith in addition to my comment, here is a modification
(edited to fully detail hopefuly)
// original allProgress
//function allProgress(proms, progress_cb) {
// let d = 0;
// progress_cb(0);
// proms.forEach((p) => {
// p.then(()=> {
// d ++;
// progress_cb( (d * 100) / proms.length );
// });
// });
// return Promise.all(proms);
//}
//modifying allProgress to delay 'p.then' resolution
//function allProgress(proms, progress_cb) {
// let d = 0;
// progress_cb(0);
// proms.forEach((p) => {
// p.then(()=> {
// setTimeout( //added line
// () => {
// d ++;
// progress_cb( (d * 100) / proms.length );
// }, //added coma :)
// 4000); //added line
// });
// });
// return Promise.all(proms
// ).then(()=>{console.log("Promise.all completed");});
// //added then to report Promise.all resolution
// }
//modified allProgress
// version 2 not to break any promise chain
function allProgress(proms, progress_cb) {
let d = 0;
progress_cb(0);
proms.forEach((p) => {
p.then((res)=> { //added 'res' for v2
return new Promise((resolve) => { //added line for v2
setTimeout( //added line
() => {
d ++;
progress_cb( (d * 100) / proms.length );
resolve(res); //added line for v2
}, //added coma :)
4000); //added line
}); //added line for v2
});
});
return Promise.all(proms
).then(()=>{console.log("Promise.all completed");});
//added then chaining to report Promise.all resolution
}
function test(ms) {
return new Promise((resolve) => {
setTimeout(() => {
console.log(`Waited ${ms}`);
resolve();
}, ms);
});
}
allProgress([test(1000), test(3000), test(2000), test(3500)],
(p) => {
console.log(`% Done = ${p.toFixed(2)}`);
});
"Promise.all completed" will output before any progress message
here is the output that I get
% Done = 0.00
Waited 1000
Waited 2000
Waited 3000
Waited 3500
Promise.all completed
% Done = 25.00
% Done = 50.00
% Done = 75.00
% Done = 100.00
Here's my take on this. You create a wrapper for the progressCallback and telling how many threads you have. Then, for every thread you create a separate callback from this wrapper with the thread index. Threads each report through their own callback as before, but then their individual progress values are merged and reported through the wrapped callback.
function createMultiThreadProgressWrapper(threads, progressCallback) {
var threadProgress = Array(threads);
var sendTotalProgress = function() {
var total = 0;
for (var v of threadProgress) {
total = total + (v || 0);
}
progressCallback(total / threads);
};
return {
getCallback: function(thread) {
var cb = function(progress) {
threadProgress[thread] = progress;
sendTotalProgress();
};
return cb;
}
};
}
// --------------------------------------------------------
// Usage:
// --------------------------------------------------------
function createPromise(progressCallback) {
return new Promise(function(resolve, reject) {
// do whatever you need and report progress to progressCallback(float)
});
}
var wrapper = createMultiThreadProgressWrapper(3, mainCallback);
var promises = [
createPromise(wrapper.getCallback(0)),
createPromise(wrapper.getCallback(1)),
createPromise(wrapper.getCallback(2))
];
Promise.all(promises);
You can use my npm package with an extended version of the native promise, that supports advanced progress capturing, including nested promises, out of the box Live sandbox
import { CPromise } from "c-promise2";
(async () => {
const results = await CPromise.all([
CPromise.delay(1000, 1),
CPromise.delay(2000, 2),
CPromise.delay(3000, 3),
CPromise.delay(10000, 4)
]).progress((p) => {
console.warn(`Progress: ${(p * 100).toFixed(1)}%`);
});
console.log(results); // [1, 2, 3, 4]
})();
Or with concurrency limitation (Live sandbox):
import { CPromise } from "c-promise2";
(async () => {
const results = await CPromise.all(
[
"filename1.txt",
"filename2.txt",
"filename3.txt",
"filename4.txt",
"filename5.txt",
"filename6.txt",
"filename7.txt"
],
{
async mapper(filename) {
console.log(`load and push file [${filename}]`);
// your async code here to upload a single file
return CPromise.delay(1000, `operation result for [${filename}]`);
},
concurrency: 2
}
).progress((p) => {
console.warn(`Uploading: ${(p * 100).toFixed(1)}%`);
});
console.log(results);
})();
const dataArray = [];
let progress = 0;
Promise.all(dataArray.map(async (data) => {
await something();
console.log('progress = ', Math.celi(progress++ * 100 / dataArray.length))
}))