Function wrapper for another functions - javascript

I have found that a lot of my API calls functions change loading property to true in the beginning and to false after it's finished. The thing is I have a lot of functions and I like to keep my code DRY.
So, I came up with something like this:
async loadingWrap (func, ...args) {
this.loading = true
await func(...args)
this.loading = false
}
and when I call it is like this:
await this.loadingWrap(
this.someAsyncFunction, { param: 'Value' }
)
where what ideally I want would be:
await this.loadingWrap(this.someAsyncFunction({ param: 'Value'}))
so it will look like a normal function to the future reader (me or someone else).
Is that possible? I looked at higher-order functions, but no luck so far.

You can almost get what you want. What you need to do is to not pass the arguments and call the function without any arguments. This behaves similarly to native functions that accept callbacks like setTimeout() or addEventListener():
async loadingWrap (func) {
this.loading = true
await func()
this.loading = false
}
Then call it similar to how you'd call functions like setTimeout():
await this.loadingWrap(() => this.someAsyncFunction({ param: 'Value'}))
The trick is to wrap your function in an anonymous function that accepts no arguments - just like other functions like it in the js world.
Here's a full working demo with console.log replacing the loading variable:
async function loadingWrap (func) {
console.log('loading');
await func()
console.log('done loading');
}
function timer (x) {
return new Promise((ok,fail) => setTimeout(ok,x));
}
async function test () {
console.log('calling async function');
await loadingWrap(() => timer(2000));
console.log('finished calling async function');
}
test();

From what you want:
await this.loadingWrap(this.someAsyncFunction({ param: 'Value'}))
This won't work because it will treat the parameter as a nested function.
The order of operations will be:
Call this.someAsyncFunction({ param: 'Value'})
Call this.loadingWrap(x) where x is the return value of step 1
This type of function evaluation is exactly like mathematical functions, where to evaluate f(g(x)) (f of g, given x), you first evaluate g given the value x, and then use the result to evaluate f.
A possible solution...
You might be able to use JavaScript's Proxy object. As the docs say, you can use them on a function by using the apply trap.
You'll write your handler generically to handle any function trying to use a loading flag.
const handler = {
apply: async function(target, thisArg, argumentsList) {
thisArg.loading = true
await target.apply(thisArg, argumentsList)
thisArg.loading = false
}
}
You will then create your someAsyncFunction member function by creating the proxy like this:
YourClass.prototype.someAsyncFunction = new Proxy(someAsyncFunction, handler);
Then you call it like this:
// inside some other async member function...
await this.someAsyncFunction({ param: 'Value'})
Here is a run-able example (there's nothing on the page, just console output):
class MyObj {
constructor() {
this.loading = false
}
async someAsyncFunction(val) {
console.log(`entering someAsyncFunction: loading = ${this.loading}`)
console.log(`calling this.asyncLoad...`)
await this.asyncLoad({
value: val
})
console.log(`exiting someAsyncFunction: loading = ${this.loading}`)
}
}
async function asyncLoad(params) {
return new Promise(resolve => {
console.log(`entering asyncLoad: loading = ${this.loading}, value = ${params.value}`)
setTimeout(() => {
console.log(`exiting asyncLoad: loading = ${this.loading}, value = ${params.value}`)
resolve()
}, 1000)
})
}
const handler = {
apply: async function(target, thisArg, argumentsList) {
console.log('PROXY: setting load to true...')
thisArg.loading = true
console.log('PROXY: calling the proxied function...')
await target.apply(thisArg, argumentsList)
console.log('PROXY: setting load to false...')
thisArg.loading = false
}
}
MyObj.prototype.asyncLoad = new Proxy(asyncLoad, handler);
async function run() {
let myobj = new MyObj()
console.log(`in run, before calling someAsyncFunction, loading = ${myobj.loading}`)
setTimeout(() => {
console.log(`INTERRUPT: checking loading is true (${myobj.loading})`)
}, 500)
await myobj.someAsyncFunction(1)
console.log(`in run, after calling someAsyncFunction, loading = ${myobj.loading}`)
}
run()
Selective Proxy-ing
If the function you're trying to call is generic enough that you only need to perform Proxy actions sometimes, this is entirely do-able. This is also where Proxy becomes really cool, because you can create different proxies to perform different actions while maintaining the same base code.
In the example below, asyncLoad is my generic function, and I can call it providing an instance of ObjWithoutStatus as the function's this context. But I also created two proxies, one to set the loading status, and another to set the loaderIsRunning status. Each of these end up calling the base function, without having to perform the gymnastics of creating wrappers that maintain the correct scope.
class ObjWithoutStatus {
constructor() {}
}
class ObjWithLoading {
constructor() {
this.loading = false
}
}
class ObjWithLoaderIsRunning {
constructor() {
this.loaderIsRunning = false
}
}
async function asyncLoad(params) {
return new Promise(resolve => {
console.log(`entering asyncLoad: loading = ${this.loading}, value = ${params.value}`)
setTimeout(() => {
console.log(`exiting asyncLoad: loading = ${this.loading}, value = ${params.value}`)
resolve()
}, 1000)
})
}
const handler_loading = {
apply: async function(target, thisArg, argumentsList) {
console.log('PROXY_loading: setting load to true...')
thisArg.loading = true
console.log('PROXY_loading: calling the proxied function...')
await target.apply(thisArg, argumentsList)
console.log('PROXY_loading: setting load to false...')
thisArg.loading = false
}
}
const handler_loaderIsRunning = {
apply: async function(target, thisArg, argumentsList) {
console.log('PROXY_loaderIsRunning: setting load to true...')
thisArg.loaderIsRunning = true
console.log('PROXY_loaderIsRunning: calling the proxied function...')
await target.apply(thisArg, argumentsList)
console.log('PROXY_loaderIsRunning: setting load to false...')
thisArg.loaderIsRunning = false
}
}
const asyncLoad_loading = new Proxy(asyncLoad, handler_loading)
const asyncLoad_loaderIsRunning = new Proxy(asyncLoad, handler_loaderIsRunning)
const x = new ObjWithoutStatus()
const y = new ObjWithLoading()
const z = new ObjWithLoaderIsRunning()
async function run() {
console.log(`in run, before calling asyncLoad, x.loading, x.loaderIsRunning = ${x.loading}, ${x.loaderIsRunning}`)
setTimeout(() => console.log(`INTERRUPT_asyncLoad: x.loading, x.loaderIsRunning = ${x.loading}, ${x.loaderIsRunning}`), 500)
await asyncLoad.call(x, {
value: 1
})
console.log(`in run, after calling asyncLoad, x.loading, x.loaderIsRunning = ${x.loading}, ${x.loaderIsRunning}`)
console.log(`in run, before calling asyncLoad_loading, y.loading = ${y.loading}`)
setTimeout(() => console.log(`INTERRUPT_asyncLoad_loading: y.loading = ${y.loading}`), 500)
await asyncLoad_loading.call(y, {
value: 2
})
console.log(`in run, after calling asyncLoad_loading, y.loading = ${y.loading}`)
console.log(`in run, before calling asyncLoad_loaderIsRunning, z.loaderIsRunning = ${z.loaderIsRunning}`)
setTimeout(() => console.log(`INTERRUPT_asyncLoad_loading: z.loaderIsRunning = ${z.loaderIsRunning}`), 500)
await asyncLoad_loaderIsRunning.call(z, {
value: 3
})
console.log(`in run, after calling asyncLoad_loaderIsRunning, z.loaderIsRunning = ${z.loaderIsRunning}`)
}
run()

It is possible, but you need to make sure the this value is correctly set when the actual function is called:
async loadingWrap (func, thisArg, ...args) {
this.loading = true;
// support functions that resolve to something useful. And provide `this`
let result = await func.apply(thisArg, args);
this.loading = false
return result;
}
And in some async method:
let result = await this.loadingWrap(
this.someAsyncFunction, this, { param: 'Value' }
);
console.log(result);
If you don't like the extra parameter, then you must pass a callback function that sets the this binding correctly, and then you might as well settle the arguments at the same time:
async loadingWrap (func) {
this.loading = true;
let result = await func();
this.loading = false
return result;
}
And in some async method, note the callback function:
let result = await this.loadingWrap(
() => this.someAsyncFunction({ param: 'Value' })
);
console.log(result);

You're looking for a higher order function, which is just a function that returns a function. Lodash uses techniques like this for functions like throttle or debounce.
// Wrap your function in another function that sets the loading property.
// We're passing "this" as "that" to preserve it when loadStuff is called.
function loadingWrap(that, functionToWrap) {
return async function() {
that.loading = true;
let returnVal = await functionToWrap.apply(that, arguments);
that.loading = false;
return returnVal;
}
}
// In your API class
public loadStuff1 = loadingWrap(this, (arg1, arg2) => {
// This method must return a promise for the loading wrap to work.
return http.get('someURL', arg1, arg2);
});
// In the class that uses your api
myAPI.loadStuff1('abc', 123);

Consider using wrapper like this:
function bar(fn) {
console.log('before');
fn();
console.log('after');
}
function baz(...params) {
console.log('inside', params);
}
bar(() => baz(1, 2, 3));
class A {
constructor() {
this.loading = false;
}
async loadable(fn) {
this.loading = true;
await fn();
this.loading = false;
}
async load() {
return new Promise(res => setTimeout(res, 2000))
}
async fetch() {
this.loadable(this.load); // or () => this.load(params)
}
}
new A().fetch();

Related

Curry function with the first one as an async function isn't working

I'm trying to curry a function but when the first one is async it throws the error function1(...) is not a function, however if I pass the async function as the last one it works fine.
Can anyone tell me why this is happening? and how to properly make a curry function that starts with a async function?
Thanks to anyone who take the time.
//This one is throwing the error: function1(...) is not a function
async function function1(path) {
const fetchedElement = await fetchElement(path);
//(...)
return (msg) => {
console.log(msg);
};
}
function1('somepath.html')('my message');
//This one works fine, properly returning other function
function function2(path) {
return async (msg) => {
const fetchedElement = await fetchElement(path);
//(...)
console.log(msg);
};
}
function2('somepath.html')('my message');
It depends on when the async work needs to get done. If you want the currying process to do the async work, then you can't invoke the currying function synchronously:
curryingFunction(paramA)(paramB)
^ assumes curryingFunction is synchronous
Instead, use two statements by the caller, one to await the currying, the other to invoke...
const fetch = path => new Promise(resolve => setTimeout(() => {
console.log('fetched ' + path)
resolve()
}, 1000));
async function curryingFunction(path) {
const fetchedElement = await fetch(path);
return message => {
console.log(message);
}
}
async function someCaller() {
// await to get a curried function, then invoke it
let curried = await curryingFunction('some_path');
curried('some message');
}
someCaller()
On the other hand, you might not need to do the async work in order to get the curried function. You probably don't. In that case, you can make the currying function synchronous, but have it return an async function that does the async work.
As a result, you'll get to use the fn()() syntax that you're probably used to using...
const fetch = path => new Promise(resolve => setTimeout(() => {
console.log('fetched ' + path)
resolve()
}, 1000));
function curryingFunction(path) {
return async (message) => {
const fetchedElement = await fetch(path);
console.log(message);
}
}
async function someCaller() {
// here we can use the fn()() syntax that we're accustomed to
await curryingFunction('some path')('some message')
}
someCaller()
As the first function is async You need to await it first, then call the second parameter.
//This one is throwing the error: function1(...) is not a function
async function function1(path) {
const fetchedElement = await Promise.resolve(1);
//(...)
return (msg) => {
console.log(msg);
};
}
(async () => {
try {
(await function1('somepath.html'))('my message');
} catch(e) {
console.log(e)
}
})()

Combine async and sync function results properly

I try to combine two function values (from a() and b()), but the code is not waiting on the await-statement in function test as expected. Instead the result value prints directly the wrong result.
function resData(status, message) {
return { ok: status, message: message };
}
function a() {
return resData(true, 'A');
}
async function b() {
// simulate some long async task (e.g. db call) and then return the boolean result
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
}); }
await sleep(2500);
return resData(true, 'B');
}
async function isValid() {
const promises = [a, b].map(async (fnc) => { return await fnc().ok; });
const results = await Promise.all(promises);
// combine the results to one boolean value
return results.every(Boolean);
}
async function test() {
// not waiting here
const res = await isValid();
// prints directly - wrong result false
console.log('result', res);
}
test();
After the wrong result output it waits 2.5 seconds. I think it has to do with the function call of resData, but I couldn't figure it out by myself, where my async / await misunderstanding is. Thanks in advance.
Instead of awaiting for the function to be resolved, You are awaiting on value return by function.
async function isValid() {
const promises = [a, b].map(async (fnc) => {
//return await fnc().ok;
// You have to await first function then call `.ok` value
return (await fnc()).ok;
});
const results = await Promise.all(promises);
// combine the results to one boolean value
return results.every(Boolean);
}
Just to simplify the problem, Please check the below code.
async function test() {
return { ok: true };
}
async function main() {
// trying to await on undefined.. test().ok == undefined
console.log(await test().ok); // undefined
// first await function.. then return ok
console.log((await test()).ok); // true
}
main();
I'd say there is 2 1 problem with your code
a was not returning a promise, so you cant treat it as one - ignore turns out you can, who knew!
You cant await a boolean, so await fnc().ok made no sense.
I would suggest you
Make a return a promise even if its just a resolved promise
Execute just the methods in the map
Read the value of ok within the every call to determine if all promises resolved with this value set to true
With these changes, it works how I suspect you expected with a 2.5 second wait before writing to the console.
function resData(status, message) {
return { ok: status, message: message };
}
function a() {
return resData(true, 'A');
}
async function b() {
// simulate some long async task (e.g. db call) and then return the boolean result
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
}); }
await sleep(2500);
return resData(true, 'B');
}
async function isValid() {
const promises = [a, b].map(fnc => fnc());
const results = await Promise.all(promises);
// combine the results to one boolean value
return results.every(x => x.ok);
}
async function test() {
// not waiting here
const res = await isValid();
// prints directly - wrong result false
console.log('result', res);
}
test();
Async functions implicitly return a promise that will ultimately be resolved to the final value returned when the function execution ends.
So, you just need to call your functions inside your map callback to collect the promises. The array of results will be an array of resData objects. So, you finally check for each ok property to match your requirements.
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
function resData(status, message) {
return {ok: status, message: message};
}
function a() {
return resData(true, 'A');
}
async function b() {
await sleep(2500);
return resData(true, 'B');
}
async function isValid() {
const promises = [a, b].map((fnc) => fnc());
const results = await Promise.all(promises);
return results.every((result) => result.ok);
}
async function test() {
const res = await isValid();
console.log('result', res);
}
test();

Override function prototype with async method, keeping this context

In javascript if i have a function defined like so
function Person() {
}
Person.prototype.someFunction = function() {
// Does soome logic
return this
}
Person.prototype.anotherFunction = function () {
// Does soome logic
return this;
};
And i want to implement chaining i will do something like this
const person = new Person();
person.someFunction().anotherFunction()
And this works as each method returns the instance of Person.
Now if i have a method which has some async action how do i return the this instsance in an async method
function someApiCall() {
return new Promise((res) => {
setTimeout(() => {
res('got data');
}, 2000);
});
}
Person.prototype.asyncFunction = function () {
someApiCall()
.then()
.catch()
// HOW DO I RETURN THIS INSTANCE HERE ???? as someAPICALL is async
};
So that i can use it as
person.someFunction().asyncFunction().anotherFunction()
Option 1 (Not executed in order):
Person.prototype.asyncFunction1 = function () {
someApiCall()
.then((e) => console.log(e))
.catch()
return this;
};
p.anotherFunction().asyncFunction1().anotherFunction()
All functions get called, but not in order. If you want to execute it in order, just do it like this:
Option 2 (Executed in order):
Person.prototype.asyncFunction2 = async function () {
const ans = await someApiCall();
console.log(ans);
return this;
};
// t represents this as you return it in asyncFunction2
p.anotherFunction().asyncFunction2().then((t) => t.anotherFunction())
You're trying to apply a synchronous programming paradigm to an asynchronous code approach, and that's just not going to work. When working with promises, which is what async does for you in an automatic way, rather than instance chaining, your code logic now needs to deal with promise chaining instead.
First, let's stop using legacy prototype syntax and look at modern (where "modern" is over five years old by now) class syntax:
class Person {
async someFunction() {
return ...
}
async anotherFunction() {
return ...
}
}
Because async is just a convenient promise wrapping, we have two options:
const person = new Person();
person
.someFunction()
.then(result => {
person
.anotherFunction()
.then(result => ...);
.catch(e => console.error(e));
})
.catch(e => console.error(e));
but this is both cumbersome and ugly. Let's use awaits instead:
const person = new Person();
try {
const someResult = await person.someFunction();
const anotherResult = await person..anotherFunction();
...
} catch (e) {
console.error(e);
}
Much better. We don't need instance chaining anymore when we're using async patterns, it's a pattern from a previous era of JS, and writing modern code does not benefit from trying to force it back in.
Some people are telling you that it can never be done or it's just not going to work. It's not their fault for misunderstanding but you don't need to suffer the same way as them.
Let' say you have an ordinary class, Account, with a few async methods -
class Account {
constructor(balance) {
this.balance = balance
}
async withdraw (x) {
await sleep(1000)
this.balance -= x
}
async deposit (x) {
await sleep(1000)
this.balance += x
}
}
sleep is a simple function which delays the program for ms milliseconds -
const sleep = ms =>
new Promise(r => setTimeout(r, ms))
Now we can write a chain function -
const chain = t =>
new Proxy(Promise.resolve(t), { get: get(t) })
const get = t => (target, prop) => (...args) =>
prop === "then"
? target[prop](...args)
: chain(target.then(async v => (await v[prop](...args), v)))
This seemingly allows us to mix synchronous and asynchronous behaviour -
const A = new Account(100)
const B = new Account(200)
chain(A).deposit(5).withdraw(20).then(a => console.log("A", a))
chain(B).withdraw(20).withdraw(30).deposit(10).then(b => console.log("B", b))
Run the snippet below to verify the result in your own browser -
const sleep = ms =>
new Promise(r => setTimeout(r, ms))
const get = t => (target, prop) => (...args) =>
prop === "then"
? target[prop](...args)
: chain(target.then(async v => (await v[prop](...args), v)))
const chain = t =>
new Proxy(Promise.resolve(t), { get: get(t) })
class Account {
constructor(balance) {
this.balance = balance
}
async withdraw (x) {
await sleep(1000)
this.balance -= x
}
async deposit (x) {
await sleep(1000)
this.balance += x
}
}
const A = new Account(100)
const B = new Account(200)
chain(A).deposit(5).withdraw(20).then(a => console.log("A", a))
chain(B).withdraw(20).withdraw(30).deposit(10).then(b => console.log("B", b))
console.log("running...")
A { balance: 85 }
B { balance: 160 }
Invent your own convenience. It's your program to do with as you please.

React setState callback return value

I'm new in React and I was looking to achieve this kind of flow:
// set the state
// execute a function `f` (an async one, which returns a promise)
// set the state again
// return the promise value from the previous function
So, what I'm doing now is the following:
async function handleSomething() {
this.setState((prevState) => { ... },
() => {
let result = await f()
this.setState((prevState) => { ... },
...
)
})
return result;
}
Hope you get the idea of what I want to achieve. Basically I want to get result, which is the value returned from awaiting f, and return it in handleSomething so I can use it in another place, but wrapping it up inside those setState calls:
// g()
// setState
// res = f()
// setState
// return res
My question is, how can I do this properly? Maybe should I modify the state with the result value and get it from there?.
EDIT:
Usage of handleSomething:
// inside some async function
let result = await handleSomething()
You can create a Promise that resolves once both setState calls are done:
function handleSomething() {
return new Promise(resolve => {
this.setState(
prevState => {
/*...*/
},
async () => {
let result = await f();
this.setState(
prevState => {
/*...*/
},
() => resolve(result)
// ^^^^^^^ resolve the promise with the final result
);
}
);
});
}
Which would be used like:
this.handleSomething().then(result => /* ... */)
// or
const result = await this.handleSomething();

Using async/await to make sequential (blocking calls) returns too early

Below is simple example to demonstrate why I am trying to do using the fetch API. I am hoping that async fetchAsync() would block till it's returning data (or an exception) but the output shows that it doesn't.
constructor
entering fetchAsync...
** we are here!!! **
leaving fetchAsync.
finish initialization
I have been trying to figure out how to display the finish string (we are here) after finish initialization when my object is done initializing with the content of the file. Isn't await/async suppose to block till it's done?
class A {
filename = "./resources/test.json";
constructor() {
console.log("constructor");
this.fetchAsync(this.filename)
.then( data => this.initialize(data)
).catch(reason => console.log(reason.message))
}
async fetchAsync(filename) {
console.log("entering fetchAsync...");
let data = await (await fetch(filename)).json();
console.log("leaving fetchAsync.");
return data;
}
initialize() {
setTimeout(() => console.log("finish initialization"), 1000)
}
}
let a = new A();
console.log("*** we are here!!! ***");
await doesn't block - that would be very user and application unfriendly - it just waits for the promise to resolve before continuing, and can't be done on the top-level (yet). If you want we are here to display only after initialization is done, then you need to be able to access a promise that resolves once initialization is done, and then call then on it.
You should also ensure initalize returns a Promise so that it can be chained to the outer call on a.
So, you could try something like this:
const dummyRequest = () => new Promise(res => setTimeout(res, 1000, 'response'));
class A {
// filename = "./resources/test.json";
constructor() {
console.log("constructor");
}
startFetch() {
return this.fetchAsync(this.filename || 'foo')
.then(data => this.initialize(data)).catch(reason => console.log(reason.message))
}
async fetchAsync(filename) {
console.log("entering fetchAsync...");
// let data = await (await fetch(filename)).json();
const data = await dummyRequest();
console.log("leaving fetchAsync.");
return data;
}
initialize() {
return new Promise(resolve => {
setTimeout(() => {
console.log("finish initialization");
resolve();
}, 1000);
});
}
}
const a = new A();
a.startFetch()
.then(() => {
console.log("*** we are here!!! ***");
});
Constructors can't be async, which is why I made the startFetch function.
You must be forgetting what asynchronous means:
class A {
constructor() {
this.filename = "./resources/test.json";
console.log("constructor");
this.fetchAsync(this.filename)
.then( data => this.initialize(data)
).catch(reason => console.log(reason.message))
}
async fetchAsync(filename){
console.log("entering fetchAsync...");
let data = await fetch(filename);
console.log("leaving fetchAsync.");
return data.json;
}
initialize() {
setTimeout(() => {console.log("finish initialization"); console.log("*** we are here!!! ***"}, 1000)
}
}
let a = new A();

Categories