do React onclick need async/await? - javascript

lets say this is what we have:
onClick={() => restrictOrders()}>
and this is our async function
const restrictOrders = async () => {
try {
const result = await axios.post(`${config.dev_server}/something`);
}catch(e){}
}
do I need to change the onClick to
onClick={async() => await restrictOrders()}>
the results is the same, I've tested it in both production and local, with high and low internet speed, added long timeouts on the server and in all cases it seems to be waiting for the response.

Short answer - no, it makes no difference.
In general, an await basically means "wait for this Promise to resolve before continuing with the rest of the code". But this means an await before the final statement of a function has no effect whatsoever, because there is nothing that needs to happen after the "wait".
As a simple illustration:
const sleep = (delay) => new Promise(resolve => setTimeout(resolve, delay));
const myFunction = async () => {
console.log("before");
await sleep(2000);
console.log("after");
};
myFunction();
and, as you would no doubt expect, there is a 2 second delay between the "before" and "after".
But if you didn't care about the "after", then you don't need the await at all:
const sleep = (delay) => new Promise(resolve => setTimeout(resolve, delay));
const myFunction = async () => {
console.log("before");
sleep(2000);
};
myFunction();
You could use await here, and maybe it's better practice in case you later want to add something after - but there's absolutely no need for it. (In this trivial case of course there's no need to have the sleep call at all - but imagine it's a "real" function with a side-effect, like posting to an API.)
It's the same with your React example:
onClick={async() => await restrictOrders()}>
you are awaiting the last (and in this case, only) statement of an anonymous function. Since there's nothing you do afterwards that needs to wait, there's no harm in not having the await there - so most commonly it's not done, I guess to make the inline function expression less verbose.

What has been observed (by me) on some of the apps is this:
Actions taken by the user (such as button-click) result in updating the state. So, in this case, if we have a state-variable like this:
const [ordersRestricted, setOrdersRestricted] = useState(0);
then, the click-handler is like so:
onClick={() => setOrdersRestricted(prev => (prev + 1))}>
Consequently, the corresponding effect (or side-effect) from the action is handled like so:
useEffect(() => {
const restrictOrders = async () => {
try {
const result = await axios.post(`${config.dev_server}/something`);
// do something with the result
// typically, update the state (but take care to not change
// dependencies, that may lead to infinite looping)
} catch(e){}
};
restrictOrders();
}, [ordersRestricted]);

Related

How to test calling to an async not-awaited function

I want to test the execution of both fuctionUT and an inner async unwaited function externalCall passed by injection. The following code is simple working example of my functions and their usage:
const sleep = async (ms) => new Promise( (accept) => setTimeout(() => accept(), ms) )
const callToExternaService = async () => sleep(1000)
const backgroundJob = async (externalCall) => {
await sleep(500) // Simulate in app work
await externalCall() // Simulate external call
console.log('bk job done')
return 'job done'
}
const appDeps = {
externalService: callToExternaService
}
const functionUT = async (deps) => {
await sleep(30) // Simulate func work
// await backgroundJob(deps.externalService) // This make test work but slow down functionUT execution
backgroundJob(deps.externalService) // I don't want to wait for performance reason
.then( () => console.log('bk job ok') )
.catch( () => console.log('bk job error') )
return 'done'
}
functionUT( appDeps )
.then( (result) => console.log(result) )
.catch( err => console.log(err) )
module.exports = {
functionUT
}
Here there is a simple jest test case that fail but just for timing reasons:
const { functionUT } = require('./index')
describe('test', () => {
it('should pass', async () => {
const externaServiceMock = jest.fn()
const fakeDeps = {
externalService: externaServiceMock
}
const result = await functionUT(fakeDeps)
expect(result).toBe('done')
expect(externaServiceMock).toBeCalledTimes(1) //Here fail but just for timing reasons
})
})
What is the correct way to test the calling of externaServiceMock (make the test pass) without slowdown the performance of the functionUT ?
I have already found similar requests, but they threat only a simplified version of the problem.
how to test an embedded async call
You can't test for the callToExternaService to be called "somewhen later" indeed.
You can however mock backgroundJob and test that is was called with the expected arguments (before functionUT completes), as well as unit test backgroundJob on its own.
If a promise exists but cannot be reached in a place that relies on its settlement, this is a potential design problem. A module that does asynchronous side effects on imports is another problem. Both concerns affect testability, also they can affect the application if the way it works changes.
Considering there's a promise, you have an option to chain or not chain it in a specific place. This doesn't mean it should be thrown away. In this specific case it can be possibly returned from a function that doesn't chain it.
A common way to do this is to preserve a promise at every point in case it's needed later, at least for testing purposes, but probably for clean shutdown, extending, etc.
const functionUT = async (deps) => {
await sleep(30) // Simulate func work
return {
status: 'done',
backgroundJob: backgroundJob(deps.externalService)...
};
}
const initialization = functionUT( appDeps )...
module.exports = {
functionUT,
initialization
}
In this form it's supposed to be tested like:
beforeAll(async () => {
let result = await initialization;
await result.backgroundJob;
});
...
let result = await functionUT(fakeDeps);
expect(result.status).toBe('done')
await result.backgroundJob;
expect(externaServiceMock).toBeCalledTimes(1);
Not waiting for initialization can result in open handler if test suite is short enough and cause a reasonable warning from Jest.
The test can be made faster by using Jest fake timers in right places together with flush-promises.
functionUT( appDeps ) call can be extracted from the module to cause a side effect only in the place where it's needed, e.g. in entry point. This way it won't interfere with the rest of tests that use this module. Also at least some functions can be extracted to their own modules to be mockable and improve testability (backgroundJob, as another answer suggests) because they cannot be mocked separately when they are declared in the same module the way they are.

JS: what's a use-case of Promise.resolve()

I am looking at https://www.promisejs.org/patterns/ and it mentions it can be used if you need a value in the form of a promise like:
var value = 10;
var promiseForValue = Promise.resolve(value);
What would be the use of a value in promise form though since it would run synchronously anyway?
If I had:
var value = 10;
var promiseForValue = Promise.resolve(value);
promiseForValue.then(resp => {
myFunction(resp)
})
wouldn't just using value without it being a Promise achieve the same thing:
var value = 10;
myFunction(10);
Say if you write a function that sometimes fetches something from a server, but other times immediately returns, you will probably want that function to always return a promise:
function myThingy() {
if (someCondition) {
return fetch('https://foo');
} else {
return Promise.resolve(true);
}
}
It's also useful if you receive some value that may or may not be a promise. You can wrap it in other promise, and now you are sure it's a promise:
const myValue = someStrangeFunction();
// Guarantee that myValue is a promise
Promise.resolve(myValue).then( ... );
In your examples, yes, there's no point in calling Promise.resolve(value). The use case is when you do want to wrap your already existing value in a Promise, for example to maintain the same API from a function. Let's say I have a function that conditionally does something that would return a promise — the caller of that function shouldn't be the one figuring out what the function returned, the function itself should just make that uniform. For example:
const conditionallyDoAsyncWork = (something) => {
if (something == somethingElse) {
return Promise.resolve(false)
}
return fetch(`/foo/${something}`)
.then((res) => res.json())
}
Then users of this function don't need to check if what they got back was a Promise or not:
const doSomethingWithData = () => {
conditionallyDoAsyncWork(someValue)
.then((result) => result && processData(result))
}
As a side node, using async/await syntax both hides that and makes it a bit easier to read, because any value you return from an async function is automatically wrapped in a Promise:
const conditionallyDoAsyncWork = async (something) => {
if (something == somethingElse) {
return false
}
const res = await fetch(`/foo/${something}`)
return res.json()
}
const doSomethingWithData = async () => {
const result = await conditionallyDoAsyncWork(someValue)
if (result) processData(result)
}
Another use case: dead simple async queue using Promise.resolve() as starting point.
let current = Promise.resolve();
function enqueue(fn) {
current = current.then(fn);
}
enqueue(async () => { console.log("async task") });
Edit, in response to OP's question.
Explanation
Let me break it down for you step by step.
enqueue(task) add the task function as a callback to promise.then, and replace the original current promise reference with the newly returned thenPromise.
current = Promise.resolve()
thenPromise = current.then(task)
current = thenPromise
As per promise spec, if task function in turn returns yet another promise, let's call it task() -> taskPromise, well then the thenPromise will only resolve when taskPromise resolves. thenPromise is practically equivalent to taskPromise, it's just a wrapper. Let's rewrite above code into:
current = Promise.resolve()
taskPromise = current.then(task)
current = taskPromise
So if you go like:
enqueue(task_1)
enqueue(task_2)
enqueue(task_3)
it expands into
current = Promise.resolve()
task_1_promise = current.then(task_1)
task_2_promise = task_1_promise.then(task_2)
task_3_promise = task_2_promise.then(task_3)
current = task_3_promise
effectively forms a linked-list-like struct of promises that'll execute task callbacks in sequential order.
Usage
Let's study a concrete scenario. Imaging you need to handle websocket messages in sequential order.
Let's say you need to do some heavy computation upon receiving messages, so you decide to send it off to a worker thread pool. Then you write the processed result to another message queue (MQ).
But here's the requirement, that MQ is expecting the writing order of messages to match with the order they come in from the websocket stream. What do you do?
Suppose you cannot pause the websocket stream, you can only handle them locally ASAP.
Take One:
websocket.on('message', (msg) => {
sendToWorkerThreadPool(msg).then(result => {
writeToMessageQueue(result)
})
})
This may violate the requirement, cus sendToWorkerThreadPool may not return the result in the original order since it's a pool, some threads may return faster if the workload is light.
Take Two:
websocket.on('message', (msg) => {
const task = () => sendToWorkerThreadPool(msg).then(result => {
writeToMessageQueue(result)
})
enqueue(task)
})
This time we enqueue (defer) the whole process, thus we can ensure the task execution order stays sequential. But there's a drawback, we lost the benefit of using a thread pool, cus each sendToWorkerThreadPool will only fire after last one complete. This model is equivalent to using a single worker thread.
Take Three:
websocket.on('message', (msg) => {
const promise = sendToWorkerThreadPool(msg)
const task = () => promise.then(result => {
writeToMessageQueue(result)
})
enqueue(task)
})
Improvement over take two is, we call sendToWorkerThreadPool ASAP, without deferring, but we still enqueue/defer the writeToMessageQueue part. This way we can make full use of thread pool for computation, but still ensure the sequential writing order to MQ.
I rest my case.

Async setTimeout on Promise based? Promise {<pending>} Error

So, I'm trying to bring my function work with async timers logic, where I need to execute computeResult (func for example) after the timer is stop. To get the setTimeout async logic under control I had used the Promise based asyncFunc function, but it always return me Promise {<pending>} when I used it.
Where is my fall in this case? Thank you.
P.S.
I also see the various posts on this topic on SoF, but it does not help me. Do not block my question just to grow up your EXP on SoF
const computeResult = () => {
return 'sdas'
}
const asyncFunc = () => new Promise(
r => setTimeout(r, 1000))
.then(() => computeResult()
);
export default asyncFunc
Not 100% sure what your trying to do,.
But the following might be what your after.
const computeResult = () => {
return 'sdas'
}
const asyncFunc = () => new Promise(resolve =>
setTimeout(() => resolve(computeResult()), 1000)
);
console.log("Wait for computeResult");
asyncFunc().then(r => console.log(r));
You write all right in this case, except one little think. You forgot to execute the Promise after it has been resolved, that's why it stuck on <pending> state.
So, in another words just write after the asyncFunc invoke the .then tail in like next way asyncFunc().then(your_sersult => ddoSomething(your_sersult))
That's all. You will get what you want :)
You can read more about it on the MDN site:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

Is there a way to write async await code that responds to onkeypress events?

I'd like to write a readKey function that's async, and then await each key being pressed in the browser.
I'd like to build this up into synchronous, normal-looking code that's all based on async-await.
So then I could write a readLine function that does await readKey() until the user hits [enter], and delete the last key if the user hits [back], etc.
And then I could write functions that await readLine(), and write functions that call them, etc.
I just don't know how to bridge the gap between writing a document.onkeypress handler... and putting the keys in that event into some async readKey function that I'd write. In other languages, I could use other multi-threading primitives to get there, but I can't figure out how to in js. I was trying to figure out if there was some way to yield the value, but I can't see how to do that, either.
Yes. Let's break it down:
Is it possible to await a custom thing?
Yes — you can await any Promise. For example, to wait for a timeout:
const timerPromise = new Promise(resolve => setTimeout(resolve, 1000));
await timerPromise;
Is it possible to have a promise for a key press?
Yes — resolve the promise when an event happens.
function readKey() {
return new Promise(resolve => {
window.addEventListener('keypress', resolve, {once:true});
});
}
Thanks to #Kornel and #Xotic750, here's what I was looking for:
const readKey = () => new Promise(resolve => window.addEventListener('keypress', resolve, { once: true }));
(async function() {
console.log('Press a key');
const x = await readKey();
console.log('Pressed', String.fromCharCode(x.which));
console.log('Press a key');
const y = await readKey();
console.log('Pressed', String.fromCharCode(y.which));
}());
This would wait until a keypress event on an element happens
await new Promise(r=>element.addEventListener('keypress', r));
Or a function to wait for any kind of events, can look like this
waitForEvent = (element, type) => new Promise(r=>element.addEventListener(type,r));
Then we can do things like
await waitForEvent(document, "load");
// or
await waitForEvent(element, "keypress");

Why does my timeout wait until after the await completes but my log does not?

If I have something like this setup:
<-- language: lang-javascript -->
console.clear();
// noprotect
const fetchSomething = () => new Promise((resolve) => {
setTimeout(() => resolve('future value'), 500);
});
async function asyncFunction() {
const result = await fetchSomething();
console.log('waiting');
setTimeout(()=>console.log('waiting?'), 250);
return result + ' 2';
}
asyncFunction().then(result => console.log(result));
And my output looks like:
"waiting"
"future value 2"
"waiting?"
I would expect the waiting? to execute before the result completes, but for some reason it waits on the function. What makes one wait but the other execute?
It is a feature of Asynchronous programming.
You have to add await and wrap your setTimeout(()=>console.log('waiting?'), 250); with a function which returns Promise in order to make it look like it was evaluated continuously.
Something like:
await ((ms) =>{
console.log('waiting?');
return new Promise(resolve => setTimeout(resolve, ms));
})(250);
Or:
await (() => new Promise(resolve => setTimeout(()=>{
console.log('waiting?');
resolve();
}, 250)))();
Mind you, JS has a single threaded run-time engine, so it interrupts evaluation when original script reaches it's end.
And function in setTimeout(function, timeout) is evaluated by JS when it has a first chance and time is right.
So your function was interrupted twice and was resumed twice.
The call to log "waiting?" is started by a setTimeout after the await has finished, so after the 500ms in fetchSomething have already passed. It will only execute 250ms after fetchSomething has returned. That is enough time for asyncFunction to return.
If you want to see a different behaviour, start the timer for logging "waiting?" before calling await:
async function asyncFunction() {
setTimeout(()=>console.log('waiting?'), 250);
const result = await fetchSomething();
console.log('waiting');
return result + ' 2';
}

Categories