Node.js: Prevent multiple simultaneous invocations of async function - javascript

In single-threaded, synchronous, non-recursive code, we can be sure that for any given function, there is never more than one invocation of it in progress at a time.
However, in the async/await world, the above no longer applies: while we're awaiting something during execution of async function f, it might be called again.
It occurred to me that, using event emitters and a queue, we could write a wrapper around an async function to guarantee that it never had more than one invocation at a time. Something like this:
const events = require('events')
function locked(async_fn) {
const queue = [] // either actively running or waiting to run
const omega = new events()
omega.on('foo', () => {
if (queue.length > 0) {
queue[0].emit('bar')
}
})
return function(...args) {
return new Promise((resolve) => {
const alpha = new events()
queue.push(alpha)
alpha.on('bar', async () => {
resolve(await async_fn(...args))
queue.shift()
omega.emit('foo')
})
if (queue.length === 1) omega.emit('foo')
})
}
}
The idea is that if f is an async function then locked(f) is a function that does the same thing except that if f is called during execution of f, the new invocation doesn't begin until the first invocation returns.
I suspect my solution has lots of room for improvement, so I wonder: is there a better way of doing this? In fact, is there one already built into Node, or available via npm?
EDIT to show how this is used:
async function f() {
console.log('f starts')
await new Promise(resolve => setTimeout(resolve, 1000))
console.log('f ends')
}
const g = locked(f)
for (let i = 0; i < 3; i++) {
g()
}
Running this takes 3 seconds and we get the following output:
f starts
f ends
f starts
f ends
f starts
f ends
Whereas if we replace g() with f() in the for loop, execution takes 1 second and get the following:
f starts
f starts
f starts
f ends
f ends
f ends
(I realise this is a fairly minor question, and if it's not appropriate for stackoverflow I apologise, but I didn't know of a better place for it.)

In case you stumble on this question, here is the code that does exactly what OP wanted:
const disallowConcurrency = (fn) => {
let inprogressPromise = Promise.resolve()
return async (...args) => {
await inprogressPromise
inprogressPromise = inprogressPromise.then(() => fn(...args))
return inprogressPromise
}
}
Use it like this:
const someAsyncFunction = async (arg) => {
await new Promise( res => setTimeout(res, 1000))
console.log(arg)
}
const syncAsyncFunction = disallowConcurrency(someAsyncFunction)
syncAsyncFunction('I am called 1 second later')
syncAsyncFunction('I am called 2 seconds later')
You also might want to change function name to something more clear, because promises have actually nothing to do with concurrency.

Here is the decorator from my previous answers: (Live Demo)
function asyncBottleneck(fn, concurrency = 1) {
const queue = [];
let pending = 0;
return async (...args) => {
if (pending === concurrency) {
await new Promise((resolve) => queue.push(resolve));
}
pending++;
return fn(...args).then((value) => {
pending--;
queue.length && queue.shift()();
return value;
});
};
}
Usage:
const task = asyncBottleneck(async () => {
console.log("task started");
await new Promise((resolve) => setTimeout(resolve, 1000));
console.log("end");
});
task();
task();
task();
task();

Can I suggest my module here: https://www.npmjs.com/package/job-pipe
Basically if you have an async method:
const foo = async () => {...}
You create a pipe for it:
const pipe = createPipe({ maxQueueSize: Infinity })
Then you wrap your method like this:
const limitedFoo = pipe(foo)
An then you can do this kind of magic:
limitedFoo()
limitedFoo()
limitedFoo()
limitedFoo()
await limitedFoo()
Even though I am awaiting only for the last one, these functions will be executed one by one due to pipe restriction.
job-pipe allows combining multiple different methods into one pipe. It allows configuration where X number of parallel jobs are permitted. Also, you can monitor how many jobs are running and how many are queued at any time. You can also choose to abort them all if needed.
I know this is an old post but I hope it will help someone.

So, it'd be a hacky way to do it, but you could also just cache the fact the function was called.
let didRun = false;
async function runMeOnce() {
if (didRun) return;
didRun = true;
... do stuff
}
await runMeOnce():
await runMeOnce(); // will just return;
I'm sure there are much better solutions - but this would work with very little effort.

Related

Promise.all vs [await x, await y] - Is it really the same? [duplicate]

This question already has answers here:
Any difference between await Promise.all() and multiple await?
(6 answers)
Closed 1 year ago.
This is a basic question, but i couldn't find the answer to it anywhere.
We have two approaches:
// consider someFunction1() and someFunction2() as functions that returns Promises
Approach #1:
return [await someFunction1(), await someFunction2()]
Approach #2:
return await Promise.all([someFunction1(), someFunction2()])
My Team Leader said that both approaches ended up in the same solution (both functions executting in parallel). But, from my knowledge, the first approach would await someFunction1() to resolve and then would execute someFunction2.
So that's the question, is it really the same, or are there any performance improvements on second approach? Proofs are very welcome!
No, you should not accept that:
return [await someFunction1(), await someFunction2()];
Is the same as:
return await Promise.all([someFunction1(), someFunction2()]);
I should also note that await in the above return await is not needed. Check out this blog post to learn more.
They are different!
The first approach (sequential)
Let's determine the difference by inspecting how each of the two alternatives works.
[await someFunction1(), await someFunction2()];
Here, in an async context, we create an array literal. Note that someFunction1 is called (a function which probably returns a new promise each time it gets called).
So, when you call someFunction1, a new promise is returned, which then "locks" the async context because the preceding await.
In a nutshell, the await someFunction1() "blocks" the array initialization until the returned promise gets settled (by getting resolved or rejected).
The same process is repeated to someFunction2.
Note that, in this first approach, the two promises are awaited in sequence. There is, therefore, no similarity with the approach that uses Promise.all. Let's see why.
The second approach (non-sequential)
Promise.all([someFunction1(), someFunction2()])
When you apply Promise.all, it expects an iterable of promises. It waits for all the promises you give to resolve before returns a new array of resolved values, but don't wait each promise resolve until waiting another one. In essence, it awaits all the promises at the same time, so it is a kind of "non-sequential". As JavaScript is single-threaded, you cannot tell this "parallel", but is very similar in the behavior point of view.
So, when you pass this array:
[someFunction1(), someFunction2()]
You are actually passing an array of promises (which are returned from the functions). Something like:
[Promise<...>, Promise<...>]
Note that the promises are being created outside Promise.all.
So you are, in fact, passing an array of promises to Promise.all. When both of them gets resolved, the Promise.all returns the array of resolved values. I won't explain in all details how Promise.all works, for that, I suggest you checking out the documentation.
You can replicate this "non-sequential" approach by creating the promises before using the await. Like so:
const promise1 = someFunction1();
const promise2 = someFunction2();
return [await promise1, await promise2];
While promise1 is being waited, promise2 is already running (as it was created before the first await), so the behavior is similar to Promise.all's.
My Team Leader said that both approaches ended up in the same solution (both functions executting in parallel).
That is incorrect.
But, from my knowledge, the first approach would await someFunction1() to resolve and then would execute someFunction2.
That is correct.
Here is a demonstration
Approach 1:
const delay = (ms, value) =>
new Promise(resolve => setTimeout(resolve, ms, value));
async function Approach1() {
return [await someFunction1(), await someFunction2()];
}
async function someFunction1() {
const result = await delay(800, "hello");
console.log(result);
return result;
}
async function someFunction2() {
const result = await delay(400, "world");
console.log(result);
return result;
}
async function main() {
const start = new Date();
const result = await Approach1();
const totalTime = new Date() - start;
console.log(`result: ${result}
total time: ${totalTime}`);
}
main();
Result is:
hello
world
result: hello,world
total time: 1205
Which means that someFunction1 runs to completion first and then someFunction2 is executed. It is sequential
Approach 2:
const delay = (ms, value) =>
new Promise(resolve => setTimeout(resolve, ms, value));
async function Approach2() {
return await Promise.all([someFunction1(), someFunction2()]);
}
async function someFunction1() {
const result = await delay(800, "hello");
console.log(result);
return result;
}
async function someFunction2() {
const result = await delay(400, "world");
console.log(result);
return result;
}
async function main() {
const start = new Date();
const result = await Approach2();
const totalTime = new Date() - start;
console.log(`result: ${result}
total time: ${totalTime}`);
}
main();
Result is:
world
hello
result: hello,world
total time: 803
Which means that someFunction2 finishes before someFunction1. The two are parallel.
Easy to see the difference
function createTimer(ms, id) {
console.log(`id: ${id} started ${new Date()}`);
return new Promise((res, rej) => {
setTimeout( () => {
console.log(`id: ${id} finished ${new Date()}`);
res(id);
}, ms );
});
}
(async function() {
var result1 = [await createTimer(5000, '1'), await createTimer(5000, '2')];
var result2 = await Promise.all([createTimer(5000, '3'), createTimer(5000, '4')]);
console.log(result1);
console.log(result2);
})();
The first one starts 1 and when 1 finishes it starts 2.
The second one starts 3 and 4 at almost the very same moment.
If you start with a function which simulates doing some work which outputs at stages of that work but takes some time. eg
function someFunction1(){
return new Promise(resolve => {
let i = 0;
const intervalId = setInterval(() => {
i++;
console.log("someFunction1", i);
if(i == 5){
clearInterval(intervalId)
resolve(1);
}
}, 1000);
});
}
And then you duplicate that with a second, similar, method. You plug your 2 methods in and you see that the one using Promise.all does it in parallel but the one using 2 await calls does it in series.
Parallel
function someFunction1(){
return new Promise(resolve => {
let i = 0;
const intervalId = setInterval(() => {
i++;
console.log("someFunction1", i);
if(i == 5){
clearInterval(intervalId)
resolve(1);
}
}, 1000);
});
}
function someFunction2(){
return new Promise(resolve => {
let i = 0;
const intervalId = setInterval(() => {
i++;
console.log("someFunction2", i);
if(i == 5){
clearInterval(intervalId)
resolve(2);
}
}, 1000);
});
}
(async function(){
const result = await Promise.all([someFunction1(),someFunction2()]);
console.log("result",result);
})();
Series
function someFunction1(){
return new Promise(resolve => {
let i = 0;
const intervalId = setInterval(() => {
i++;
console.log("someFunction1", i);
if(i == 5){
clearInterval(intervalId)
resolve(1);
}
}, 1000);
});
}
function someFunction2(){
return new Promise(resolve => {
let i = 0;
const intervalId = setInterval(() => {
i++;
console.log("someFunction2", i);
if(i == 5){
clearInterval(intervalId)
resolve(2);
}
}, 1000);
});
}
(async function(){
const result = [await someFunction1(),await someFunction2()];
console.log("result",result);
})();
Both give the exact same result but getting there is very different.
MDN documentation for Promise.all() states that
This method can be useful for aggregating the results of multiple promises. It is typically used when there are multiple related asynchronous tasks that the overall code relies on to work successfully — all of whom we want to fulfill before the code execution continues.
While it isn't explicit, you can await Promise.all to track multiple promises. Only when all promises are resolved will the code execution continue.
The other approach you mention of capturing separate asynchronous tasks in an array is not the same due to how await operates.
An await splits execution flow, allowing the caller of the async function to resume execution. After the await defers the continuation of the async function, execution of subsequent statements ensues. If this await is the last expression executed by its function, execution continues by returning to the function's caller a pending Promise for completion of the await's function and resuming execution of that caller.
So, each await will pause execution before resuming. No need for a demonstration.

Resolve promise on array push - javascript

I'm attempting to define a function that returns a promise. The promise should resolve when a given array is set (push()).
To do this I'm attempting to use a Proxy object (influenced by this):
let a = []
;(async function(){
const observe = array => new Promise(resolve =>
new Proxy(array, {
set(array, key, val) {
array[key] = val;
resolve();
}
}));
while(true){
await observe(a);
console.log(new Date().toLocaleTimeString(),"Blimey Guv'nor:",`${a.pop()}`);
}
})(a);
;(async function(){
await new Promise(resolve => timerID = setTimeout(resolve, 2000))
a.push('ʕ·͡ᴥ·ʔ');
a.push('¯\(°_o)/¯ ')
})(a)
I can't see why this doesn't work. Does anyone have any idea?
More generally, what is a good way to have a promise resolve on push to an array?
The problems with your attempt:
you invoke .push on the original array, not the proxied one. Where you create the proxy, it is returned to no-one: any reference to it is lost (and will be garbage collected).
The code following after the line with await will execute asynchronously, so after all of your push calls have already executed. That means that console.log will execute when the array already has two elements. Promises are thus not the right tool for what you want, as the resolution of a promise can only be acted upon when all other synchronous code has run to completion. To get notifications during the execution synchronously, you need a synchronous solution, while promises are based on asynchronous execution.
Just to complete the answer, I provide here a simple synchronous callback solution:
function observed(array, cb) {
return new Proxy(array, {
set(array, key, val) {
array[key] = val;
if (!isNaN(key)) cb(); // now it is synchronous
return true;
}
});
}
let a = observed([], () =>
console.log(new Date().toLocaleTimeString(),"Blimey Guv'nor:", `${a.pop()}`)
);
a.push('ʕ·͡ᴥ·ʔ');
a.push('¯\(°_o)/¯ ');
As noted before: promises are not the right tool when you need synchronous code execution.
When each push is executed asynchronously
You can use promises, if you are sure that each push happens in a separate task, where the promise job queue is processed in between every pair of push calls.
For instance, if you make each push call as part of an input event handler, or as the callback for a setTimeout timer, then it is possible:
function observed(array) {
let resolve = () => null; // dummy
let proxy = new Proxy(array, {
set(array, key, val) {
array[key] = val;
if (!isNaN(key)) resolve();
return true;
}
});
proxy.observe = () => new Promise(r => resolve = r);
return proxy;
}
let a = observed([]);
(async () => {
while (true) {
await a.observe();
console.log(new Date().toLocaleTimeString(),"Blimey Guv'nor:",`${a.pop()}`);
}
})();
setTimeout(() => a.push('ʕ·͡ᴥ·ʔ'), 100);
setTimeout(() => a.push('¯\(°_o)/¯ '), 100);

How to create a sequential promise loop with variable function calls

I need to somehow loop over the work array passed to _start then
for each of the items in the array, I need to somehow call the corresponding function with the same name.
I don't have control over the number of items in work the array or the number of items, I do know that there will always be a corresponding function.
I don't want to call all the functions at the same time, once the first function resolves after 3 seconds, I then want to call the second function, once the second function resolves after 3 seconds I then want to call the third function. Once the third function resolves after another 3 seconds I want to call _done().
In this example each function takes 3 seconds to complete _done wont gete called for 9 seconds.
function _start(data){
// Insert some kinda native magic loop
}
function _one(){
return new Promise((resolve, reject) => {
setTimeout(function(){
resolve(1);
}, 3000);
})
};
function _two(){
return new Promise((resolve, reject) => {
setTimeout(function(){
resolve(2);
}, 3000);
})
};
function _done(){
console.log('All done in 9 seconds)
}
(function(){
var work = ['_one', '_two', '_two'];
_start(work);
})();
Given the order is dicated by the array, you can use reduce to aggregate the promises into a chain
const _start = (...actions) => {
return actions.reduce((chain, action) => {
const func = this[action];
return chain.then(() => func());
}, Promise.resolve());
}
...
_start('_one', '_two', '_three').then(() => console.log('All done'));
See it in action - the example appends an extra then to the chain just to output any results from the promises (probably outwith the scope of this question but something you may have to consider if getting data back is required).
Update
Can see you intend on invoking _start from a different context in which the functions are declared, this is fine but you need to make sure you set the correct context before hand i.e.
const self = this;
(function() {
_start.bind(self)('_one', '_two', '_two');
})();
A function which creates a promise which sleeps:
const sleep = n => () => new Promise(resolve => setTimeout(resolve, n));
A function which sleeps after some input promise:
const sleepAfter = n => p => p.then(sleep(n));
A function which chains a bunch of promises, represented by functions:
const chain = (...promises) => promises.reduce((ret, promise) => ret.then(promise),
Promise.resolve());
Run a bunch of functions yielding promises, sleeping in between:
const _start = promises => chain(promises.map(sleepAfter(3000)));
Now just:
_start(_one, _two, _three).then(_done);
Try using this:
_one().then((firstResponse) {
return_two();
}) .then((secondResponse) => {
*second and first respone are already done*
});
Use promises then
_one().then((responseOne) => {
return _two();
}).then((responseTwo) => {
// _one & _two are done
});

How to measure the execution time of a promise?

I'm trying to write a function that measures the execution time of another function:
export class Profiler {
public measureSyncFunc(fn: () => any): Promise<number> {
return new Promise<number>((resolve, reject) => {
let elapsed = 0;
let intervalId = window.setInterval(() => {
elapsed += 1; // this is never called
}, 1);
this.execFunc(fn)
.then((result: any) => {
window.clearInterval(intervalId);
resolve(elapsed);
});
});
}
private execFunc(fn: () => any): Promise<any> {
return new Promise<any>((resolve, reject) => {
resolve(fn());
});
}
}
Then I use it like that:
let array = generateRandomArray(100000);
instance.measureSyncFunc(bubbleSort(array))
.then((elapsed: number) => {
console.log(`end session: ${elapsed} seconds`);
resolve();
});
The bubbleSort function is synchronous and it takes several seconds to complete.
See code here:
The result in the console is "end session: 0 seconds" because the interval callback is never called.
Do you know how I can make it called ?
Thank you very much guys !
If the functions you want to measure will always be synchronous there's really no need to involve promises.
Since the function you want to test takes parameters you it's best to to wrap it in an arrow function in order to be able to call it with another context and not have to manage it's parameters yourself.
Something simple like this will do just fine.
function measure(fn: () => void): number {
let start = performance.now();
fn();
return performance.now() - start;
}
function longRunningFunction(n: number) {
for (let i = 0; i < n; i++) {
console.log(i);
}
}
let duration = measure(() => {
longRunningFunction(100);
});
console.log(`took ${duration} ms`);
If you want to measure the time it takes an async function (if it returns a promise) to resolve you can easily change the code to something like this:
function measurePromise(fn: () => Promise<any>): Promise<number> {
let onPromiseDone = () => performance.now() - start;
let start = performance.now();
return fn().then(onPromiseDone, onPromiseDone);
}
function longPromise(delay: number) {
return new Promise<string>((resolve) => {
setTimeout(() => {
resolve('Done');
}, delay);
});
}
measurePromise(() => longPromise(300))
.then((duration) => {
console.log(`promise took ${duration} ms`);
});
Note: This solution uses the ES6 Promise, if you are using something else you might have to adapt it but the logic should be the same.
You can see both examples working in the playground here.
Don't use setInterval to count milliseconds (It's inaccurate, lags, drifts and has a minimum interval of about 4ms). Just get two timestamps before and after the execution.
function measureAsyncFunc(fn: () => Promise<any>): Promise<number> {
const start = Date.now();
return fn.catch(() => {}).then(() => {
const end = Date.now();
const elapsed = end-start;
return elapsed;
});
}
For higher accuracy, replace Date.now by performance.now.
Have a look at timeFnPromise and the related test cases.
target function is wrapped and executed when the wrapped function is called
appends fulfillment / rejection handler to the underlying Promise that returns the target functions return value as "ret" and the elapsed time as "elapsedTime"
supports arguments by passing them through to the target function
Samples Usage:
const wrappedFn = timeFnPromise(aFunctionThatReturnsAPromise)
wrappedFn()
.then((values)=>{
const {ret, elapsedTime} = values
console.log(`ret:[${ret}] elapsedTime:[${elapsedTime}]`)
})
Also available via NPM module jschest.
Here's a simple wrapper function I wrote. It returns a Promise (via the async keyword), and so you can just call it with your promise. I added the time value as a property to the response. If you cannot have that value in the response, then you would need to remove it afterwards.
const stopwatchWrapper = async (promise) => {
const startTime = Date.now()
const resp = await promise
resp.executionTime = Date.now() - startTime
return resp
}
const axiosPromise = stopwatchWrapper(axios(reqSelected))
const response = await axiosPromise
console.log(response.executionTime)
It would be good to clarify that the proposed approaches by toskv only work with the resolution of a single promise. If we want to use Promise.all() the time result it returns is wrong.
Here is an example with the code that toskv developed, but using Promise.all()
Measure with Promise.all()
If someone needs to measure the time it takes to execute each of the promises executed with a Promise.all() the approach that can be followed is to make use of the interceptors and do the time measurements there

Can I force the resolution of a promise to await results in javascript?

This question is somewhat academic in that I don't have a real need to do this.
I'm wondering if I can force the resolution of a promise into a returned value from a function such that the function callers are not aware that the functions contain promised async operations.
In .NET I can do things like this by using functions on Task[] or return Task.Result which causes the caller to await the completion of the task and callers won't know or care that the work has been done using tasks.
If you're using ES6 you can use a generator to make code like this. It essentially comes close to 'blocking' on the promise, so you have the appearance of a long-running method that just returns the value you want, but async/promises live under the covers.
let asyncTask = () =>
new Promise(resolve => {
let delay = Math.floor(Math.random() * 100);
setTimeout(function () {
resolve(delay);
}, delay);
});
let makeMeLookSync = fn => {
let iterator = fn();
let loop = result => {
!result.done && result.value.then(res =>
loop(iterator.next(res)));
};
loop(iterator.next());
};
makeMeLookSync(function* () {
let result = yield asyncTask();
console.log(result);
});
More explanation and the source available here: http://www.tivix.com/blog/making-promises-in-a-synchronous-manner/
Here is the code compiled on Babeljs.io

Categories

Resources