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.
Related
When I write an async function it usually returns a promise:
export const myPromiseFunction = async (params) => {
// some logic
return Promise.resolve('resolved-value');
});
But I was wondering if it would be a mistake if this function would not return a promise, so for example:
export const myPromiseFunction = async (params) => {
// some logic
params.map(async (param) => {
await printParam(param);
async function printParam(par) {
// do some other stuff
Printer.print(par);
});
});
});
export class Printer {
public static async print(par) {console.log(par);} // I know it could not be async, but for the sake lets suppose it does
}
Is this a mistake / bad practice ? Or can we find a scenario when this will be valid and desirable ?
All async functions automatically return Promises. If you declare a function as async, it will return a Promise, even if your only return value is a simple value like a string or number. If you don't return explicitly, your async function will still return a Promise with a value of undefined.
In fact, it is more common for the body of an async function to return a simple value rather than a promise; the assumption is that your function is async because you await the promises you consume within it. As a consequence, even if you return the value 5, the return value is a Promise (that resolves to 5) representing the potential delay that comes from any await expressions in the function.
You don't have to return a Promise object explicitly in your async function, and it is redundant to do so if you're just wrapping a simple value like 'resolved-value'. Conversely, you can make a normal function behave like an async function if you always return a Promise (potentially with Promise.resolve) and you never synchronously throw an error within it.
async function myFunction() {
makeSomeCall(); // Happens immediately.
await someOtherPromise(); // myFunction returns a Promise
// before someOtherPromise resolves; if it
// does without error, the returned Promise
return 5; // resolves to 5.
}
/** Behaves the same as the above function. */
function myFunctionButNotAsync() {
try {
makeSomeCall();
// If you didn't have someOtherPromise() to wait for here, then
// this is where Promise.resolve(5) would be useful to return.
return someOtherPromise().then(() => 5);
} catch (e) {
return Promise.reject(e);
}
}
All that said, you may have occasion to explicitly return a Promise object (such as one produced by Promise.all or a separate Promise-returning function), which then observes rules similar to what Promise.resolve() observes: If the object you return from an async function is a Promise, or has a then function, then the automatic Promise the async function returns will wait for the specific Promise or Promise-like object you pass back with return.
async function myFunction() {
makeSomeCall(); // Happens immediately.
await anythingElse(); // You can still await other things.
return someOtherPromise(); // The promise myFunction returns will take
// the same outcome as the Promise that
// someOtherPromise() returns.
}
In a related sense, this is why return await is seen as redundant, though as described it does make a difference for the stack traces that you see if the wrapped promise is rejected.
Short answer: no, an async function doesn't have to returns a Promise. Actually, generally you wouldn't return a Promise object (unless you're chaining asynchronous events).
What async and await do is wait for a response from something that returns a Promise.
You first code example actually returns a resolved Promise. But what happens if the Promise isn't resolved properly ?
It's best to call a function that returns a Promise from another async function:
function getRequestResult() {
return new Promise(resolve => {
setTimeout(() => {
resolve('request sent');
}, 2000);
});
}
async function sendMyRequest() {
console.log('Sending request');
const result = await getRequestResult();
console.log(result);
// expected output: "resolved"
}
You can send rejections/errors within getRequestResult() that way, and also manage how these errors will be managed by the call in sendMyRequest() (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await).
Basically Visual Studio Code gives me the note 'await' has no effect on the type of this expression. but I am note able to track the actual problem down. Of course my example here is a bit nested and therefor a bit more complex than it maybe has to be. It all starts with an array where I want to check it a value is valid. If on value fails the test, the .every function should end.
myList.every(function (value) {
if(value == null) return false;
//other stuff for preparation
return _handleValue(value);
});
value is a complex datatype so the _handleValue function is splitted into different subfunctions. Some of the function are using promises some (atm) don't. Please ignore the functions in the if block because the code is simplified and this is not causing the problem.
async function _handleValue(value) {
if (somePreChecks(value)) {
return false;
}
else if (valueIsType(value)) {
return await _someHandlerwithPromise(value); //<-- Using await here.
} else if (valueIsOtherType(value)) {
return _someOtherHandlerWithoutPromise(value);
}
}
The function _someHandlerWithPromise has some call which are using callback. Anyway this should not be a problem as long as I call reject().
function _someHandlerWithPromise(value) {
return new Promise(resolve => {
foo.acceptValue(value, function (data) {
_updateSystem(data, function (err, data) {
if(err){
//Do something
}
resolve(true);
});
});
});
}
Actually this worked pretty well elsewhere where I do not use the await function. But because the .every is not able to handle asynchronous methods, I am forced to use await.
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'm trying to learn async-await. In this code -
const myFun = () => {
let state = false;
setTimeout(() => {state = true}, 2000);
return new Promise((resolve, reject) => {
setTimeout(() => {
if(state) {
resolve('State is true');
} else {
reject('State is false');
}
}, 3000);
});
}
const getResult = async () => {
return await myFun();
}
console.log(getResult());
why am I getting output as -
Promise { <pending> }
Instead of some value? Shouldn't the getResult() function wait for myFun() function resolve it's promise value?
If you're using async/await, all your calls have to use Promises or async/await. You can't just magically get an async result from a sync call.
Your final call needs to be:
getResult().then(response => console.log(response));
Or something like:
(async () => console.log(await getResult()))()
What you need to understand is that async/await does not make your code run synchronously, but let's you write it as if it is:
In short: The function with async in front of it is literally executed asynchronously, hence the keyword "async". And the "await" keyword wil make that line that uses it inside this async function wait for a promise during its execution. So although the line waits, the whole function is still run asynchronously, unless the caller of that function also 'awaits'...
More elaborately explained: When you put async in front of a function, what is actually does is make it return a promise with whatever that function returns inside it. The function runs asynchronously and when the return statement is executed the promise resolves the returning value.
Meaning, in your code:
const getResult = async () => {
return await myFun();
}
The function "getResult()" will return a Promise which will resolve once it has finished executing. So the lines inside the getResult() function are run asynchronously, unless you tell the function calling getResult() to 'await' for it as well. Inside the getResult() function you may say it must await the result, which makes the execution of getResult() wait for it to resolve the promise, but the caller of getResult() will not wait unless you also tell the caller to 'await'.
So a solution would be calling either:
getResult().then(result=>{console.log(result)})
Or when using in another function you can simply use 'await' again
async function callingFunction(){
console.log(await(getResult());
}
This is my routine dealing with await and async using a Promise with resolve and reject mechanism
// step 1 create a promise inside a function
function longwork()
{
p = new Promise(function (resolve, reject) {
result = 1111111111111 // long work here ;
if(result == "good"){
resolve(result);
}
else
{
reject("error ...etc")
}
})
return p
}
// step 2 call that function inside an async function (I call it main)and use await before it
async function main()
{
final_result = await longwork();
//..
}
//step 3 call the async function that calls the long work function
main().catch((error)=>{console.log(error);})
Hope that saves someone valuable hours
What hasn't been mentioned in this discussion are the use-case implications of the behaviour. The key thing, as I see it, is to consider what you are planning to do with the output from the top level, truly asynchronous function, and where you are planning to do that.
If you are planning to consume the output immediately, i.e. within the "async" function that is awaiting the return of the top level asynchronous function, and what you do with the output has no implication for other functions deeper in the call stack, then it does not matter that the deeper functions have moved on. But if the output is needed deeper in the call stack, then you need use "async" functions making await calls all the way down the stack to that point. Once you reach a point in the call stack where the function does not care about the asynchronous output, then you can stop using async functions.
For example, in the following code, function B uses the stuff returned from function A so is declared "async" and awaits A(). Function C() calls B(), is returned a Promise, but can move straight on before that promise is resolved because it is not interested in A()'s stuff, nor what's done with it. So C does not need to be declared as async, nor await B().
function A() {
return new Promise((resolve, reject) => {
//do something slow
resolve (astuff)
}
}
async function B() {
var bstuff = await A();
dosomethingwith(bstuff);
return;
}
function C() {
B();
dontwaitmoveon();
...
return;
}
In this next example, C() does use A()'s stuff, so needs to wait for it. C() must be declared "async" and await B(). However D() does not care about A()'s stuff, nor what's done with it, so moves on once C() returns its promise.
function A() {
return new Promise((resolve, reject) => {
//do something slow
resolve (astuff)
}
}
async function B() {
var bstuff = await A();
dosomething();
return bstuff;
}
async function C() {
var cstuff = await B();
dosomethingwith(cstuff);
...
return;
}
function D() {
C();
dontwaitmoveon();
...
return;
}
Since figuring this out, I have tried to design my code so the stuff returned by the asynchronous function is consumed as close as possible to the source.
Though your "getResult" function is async and you have rightly made an await call of myFun, look at the place where you call the getResult function, it is outside any async functions so it runs synchronously.
So since getResult called from a synchronous point of view, as soon as it is called, Javascript synchronously gets whatever result is available at the moment, which is a promise.
So returns from an async function cannot be forced to await(very important), since they are synchronously tied to the place of origin of the call.
To get what you want you can run the below,
async function getResult() {
const result = await myFun();
console.log(result);
//see no returns here
}
getResult();
I want to have some mechanism to redo then function in promise. Keep doing it until it pass some check
somePromise.then(() => {
if(state == "finish")
return;
// DoSomething async then repeat these codes
// could we do it by [return this;] ?
}).then(() => console.log("finish"));
In general, you don't get access to the current promise inside the .then() handler. And, for the most part, you couldn't do anything useful anyway. Adding another .then() handler does not do anything different than the .then() handler you're already in. Once fulfilled, promises never change their state.
If you want to repeat something, you make a function out of the thing you want to repeat and you call it over again from within the .then() handler and you return the resulting promise (thus chaining it to the previous promise).
Here's an example:
// utility delay function that returns a promise
function delay(t, val) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve(val);
}, t);
});
}
// repeatedly call someAsyncOp() until we see the operation is finished
function repeatUntil(t) {
return someAsyncOp().then(function(state) {
if (state !== "finish") {
return delay(t).then(repeatUntil);
}
});
}
// sample usage
repeatUntil(5000).then(function() {
// state is finished here
}).catch(function(err) {
// error here
});
You can name the function used at .then(), recursively call same function at .then() of doSomethingAsync() call until state == "finish"
// name function used at `.then()`
somePromise.then(function re(data) {
if(state == "finish") {
return data;
};
// DoSomething async then repeat these codes
// call `re` at `.then()` chained to `doSomethingAsync()`
doSomethinAsync(data).then(re);
}).then((data) => console.log("finish", data));
See also multiple, sequential fetch() Promise