I'm building an app with Cordova. On one page, there is an EventListener that calls an async function. window.addEventListener("load", loadEdit(), false);
The function looks like this async function loadEdit(){...}.
When testing in the browser I get the following error even though the function is fully executed:
TypeError: Property 'handleEvent' is not callable.
However, if I change the EventListener to another function which then calls the async function, there seems to be no problem.
For example:
window.addEventListener("load", loadPre(), false);
...
function loadPre()
{
loadEdit();
}
What is the problem with an async function getting called by the EventListener?
Why does it not detect that the second method also calls an async function?
You can call an async function from your EventListener.
The first problem I see is that you are invoking the callback function right away in the second argument of window.addEventListener by including it as loadEdit() instead of loadEdit or () => loadEdit(). You have to give it a function as second argument, right now you are giving a Promise or the return value of loadPre().
Try this way:
window.addEventListener("load", () => loadEdit(), false);
async function loadEdit() {
// do the await things here.
}
Async function return Promises. So, if you would like to do something after loadEdit, try:
window.addEventListener("load", () => {
loadEdit().then(/* callback function here */);
}, false);
async function loadEdit() {
// do the await things here.
}
TL&DR: In order to use a call-back with an async function, you will need to decorate your async function within the Add Listener, and use await within the code block;
Explanation: As mentioned above in your example you are calling the function immediately; This means either two things will happen: Your function will return a value immediately OR you will get a promise; In either case its best to 'await' the call so the program runs more synchronously.
async function getStringSize(searchString) {
return searchString.length;
}
txtInput.addEventListener("keyup", async e => {
const searchString = e.target.value;
total_length = await getStringSize(searchString);
if (total_length > 3) {
console.log(searchString);
}
}, false);
Related
How to call async function from regular sync function?
getCategoryAll() doesn't work on page load as it works correctly when I type it into console.
What I should change to make my function call giving an output that I expect?
async function getCategory(catId) {
const response = await axios.get...
// my code here
}
function getCategoryAll() {
// my for loop logic here referring to getCategory(catId)
}
getCategoryAll()
Right now solution what I have is delaying execution of 2nd function which is obviously not the best solution
setTimeout(() => getCategoryAll(), 500);
as below, an async function was given for a paramter where non-async function expected, but it worked, why?
function getCallback(param, callback) {
setTimeout(() => {
callback && callback(param);
}, 1);
}
getCallback(13, async (param) => {
console.log(param);
});
---
13
It worked, why?
Because an async function is just a function that returns a promise and can be called like any other function. Your getCallback ignores the return value of callback(), so it doesn't matter what it returns.
Is it ok to pass async function where no async function is needed?
No, it generally is not ok to pass an async function (or another function that returns a promise) to a function that doesn't expect the callback to return a promise. Promises should not be ignored, they should be .then()-chained or awaited, and they need special error handling. If the getCallback does not handle (promise) errors, you must not pass a callback that errors.
This question already has answers here:
Async function without await in JavaScript
(4 answers)
Closed 2 years ago.
due to everyone's help, I got the logic behind async, promise, then, await.
I have one curisosity on top of its basic nature, which is what if
I declare async function but doesn't use await in it.
technically every argument within async function is invisibly capsulated by '.then()'
but how it works would be exactly same as synchronous function execution.
For example,
async function trackUserHandler() {
var positiondata= await getPosition();
var timerdata = await setTimer(2000)
console.log(positiondata, timerdata);
setTimer(1000).then(() => {
console.log('Timer done!');
});
console.log('one');
}
The console below doesn't run till the first two await function is done due to await(s) sitting before this.
console.log(positiondata, timerdata);
What if I don't put any await(s) in the async like below?
async function trackUserHandler() {
var positiondata= getPosition();
var timerdata = setTimer(2000)
console.log(positiondata, timerdata);
setTimer(1000).then(() => {
console.log('Timer done!');
});
console.log('one');
}
I test-run this code and behaves seemigly same as regular function without 'async'.
Of course behind the scene, everything in the function is encapsulated into 'then()' though.
Am I understanding right?
Thank you in advance.
Behind the scene- If I use Aync but doesn't use await in it, would it be identical with normal function?
Yes, you are right. An async function without an await expression will run synchronously and would be identical with normal function, but the only difference is that async functions always return a promise. If the return value of an async function is not explicitly a promise, it will be implicitly wrapped in a promise.
For example, the following:
async function foo() {
return 1
}
...is equivalent to:
function foo() {
return Promise.resolve(1)
}
If there is an await expression inside the function body, however, the async function will always complete asynchronously.
For example:
async function foo() {
await 1
}
...is equivalent to:
function foo() {
return Promise.resolve(1).then(() => undefined)
}
Code after each await expression can be thought of as existing in a .then callback.
Yes, an async function runs synchronously till the first await, so it behaves like a regular function. It does return a Promise though:
function trackUserHandler() {
// ... code
return Promise.resolve(undefined);
}
In your example, the 2 functions won't behave the same. Without the await keyword, your variables won't capture the results returned by these 2 functions, but instead receive 2 Promises.
var positiondata = getPosition();
var timerdata = setTimer(2000);
So your console.log will print out 2 Promises instead of the values you actually expect.
An async function can contain an await expression, that pauses the
execution of the async function and waits for the passed Promise's
resolution, and then resumes the async function's execution and
returns the resolved value.
As you assumed, if no await is present the execution is not paused and your code will then be executed in a non-blocking manner.
const getPromise = async (s) => {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(s), 500);
});
}
(async() => {
try {
const result = getPromise("a"); //no await, result has not been unwrapped
console.log('async/await -> ', result);
} catch (err) {
console.log(err);
}
})();
Async function without await in Javascript
I have troubles to unterstand how the following function from this online javascript book (You-Dont-Know-JS) works? How does this function enable async behaviour when passed to a third party function (described in the next snippet) that may or may not execute async?
function asyncify(fn) {
var orig_fn = fn,
intv = setTimeout( function(){
intv = null;
if (fn) fn();
}, 0 )
;
fn = null;
return function() {
// firing too quickly, before `intv` timer has fired to
// indicate async turn has passed?
if (intv) {
fn = orig_fn.bind.apply(
orig_fn,
// add the wrapper's `this` to the `bind(..)`
// call parameters, as well as currying any
// passed in parameters
[this].concat( [].slice.call( arguments ) )
);
}
// already async
else {
// invoke original function
orig_fn.apply( this, arguments );
}
};
}
The link I provided also lists the following code example on how to execute asyncify(..):
function result(data) {
console.log( a );
}
var a = 0;
ajax( "..pre-cached-url..", asyncify( result ) );
a++;
Here ajax(..) is a third party function that may execute async or sync. To enforce an async callback of result(..) the author introduces asyncify(..) as an example. This will result in the console output always being 1 (async).
As far as I understand, the function fist defines two variables orig_fn to store the original function handle, result() in this case. And intv is the timer id returned from setTimeout.
var orig_fn = fn,
intv = setTimeout( function(){
intv = null;
if (fn) fn();
}, 0 )
;
setTimeout(..,0) means what exactly? To execute immediately in the next run of the event loop?
Why do we execute fn() in the callback function of setTimeout and the else statement in the return function? Isn't the function called twice this way when ajax operates async? First when the timer fires, which calls fn() and again when invoking orig_fn.apply(this, arguments)?
Why is asyncify returning a function but also calling the function fn passed to it?
What does the following in the if statement mean and does fn even get called if ajax is synchronous?
fn = orig_fn.bind.apply(
orig_fn,
// add the wrapper's `this` to the `bind(..)`
// call parameters, as well as currying any
// passed in parameters
[this].concat( [].slice.call( arguments ) )
);
Why is the parameter fn set to the original function? For me the variable fn is not even used after returning? Or is it a reference to result() which is now changed by adding this of the wrapper and the arguments? If so, why is this of the wrapper needed?
I guess my main problem to understand this function is how the flow of this code works depending on how ajax(..) works (async or sync). Could someone please explain both cases with the steps that happen in between?
I have async function that returns true or false. It looks like following one:
class User {
async canManageGroup(group) {
if (typeof group === 'number') {
// group - id
group = await getGroupById(group)
} // else group is already loaded from DB
return this.id === group.manager.id
}
}
If group parameter is ID of group then function will make async call to DB, so function canManageGroup will execute asynchronously. But if group parameter is group model then function will only call return this.id === group.manager.id or it will execute syncronously. Is it good practice to write code in this way? Or I should transform synchronous code to asynchronous?
function makeAsync(cb) {
return new Promise(resolve => setImmediate(() => resolve(cb())))
}
class User {
async canManageGroup(group) {
if (typeof group === 'number') {
// group - id
group = await getGroupById(group)
} // else group is already loaded from DB
return await makeAsync(() => this.id === group.manager.id)
}
}
You can use the first example without problems.
When you use async, your function will return a Promise. If your code is sync, the status of the returned promise will be resolved and it is safe to run any promise-related code on it (then, catch, etc).
For example:
async function truePromise() {
return true;
}
truePromise().then(function(value){
console.log("Promise value:", value);
});
Just works :)
Should you do it?
Yes. It is okay and works okay thanks to the async keyword.
What you MUST NOT do is the following:
function dontDoIt(doSync) {
if (doSync) return false;
return Promise.resolve(false)
}
Why? Because:
if doSync is truhtly, it will return false (i.e a boolean)
if doSync is falsy, it will return a Promise that will resolve to false.
That is a HUGE difference.
Why?
Your function sometimes returns a promise, and other times return a boolean. It is inconsistent.
doSync(true) is a boolean, you can't use await nor .then.
doSync(false) is a Promise, you can use await and then.
Based on your comment to one of the answers you are concerned because of the answer to the question How do you create custom asynchronous functions in node.js?
The problem is, this function is inconsistent: sometimes it is asynchronous, sometimes it isn't. Suppose you have a consumer like this:
In js enviroments like nodejs is common practice that the callback that could be executed async, should always be called async, no matter if the actual code was really async.
So you are wondering if the following code will break that common practie.
async function doSomething() {
return true
}
doSomething()
.then(res => {
console.log(res)
})
This is not the case, because here you deal with Promises and a Promise is allowed to be resolve immediatly, the code above is basicly the same as if you would write:
Promise.resolve(true)
.then(res => {
console.log(res)
})
The async behaviour is ensured in the chaining part (then/catch), the callback you pass to the then/catch will be called async:
async function doSomething() {
return true
}
console.log('before');
doSomething()
.then(res => {
console.log('with in callback')
})
console.log('after');
As you can see the order of the logs is:
before
after
with in callback
So this is in the same order you would expect from an regular async callback function.
The first one is correct. There is no need to force anything to async if there doesn't need to be. Context switching isn't free, so if it doesn't have to wait for anything to complete, don't try and make it.