Why can't I access the variable field_count outside of loadData() function?
var field_count;
await loadData();
async function loadData() {
var response = await fetch('/get_data');
var data = await response.json();
field_count = data.field_count;
alert(field_count) // shows correct data
}
alert(field_count) //shows undefined
Here loadData() is an async function, but your alert(field_count) outside of any function is not async. You're doing two await() calls inside the function, which operate asynchronously as you've requested of the language. However, the alert() which is outside the function is operating synchronously. Under most circumstances, that top-level alert() will have run and tried to alert() an undefined value before the two await calls are completed.
Mixing async programming and synchronous programming in this way is fraught with traps and pitfalls. I would recommend until you are comfortable with the ideas of asynchronous code and how it differs from other code, set up the program first then stick to async code working with other async code. Once you are comfortable with both and understand the differences, you can consider using results from async code in sync code, but you'll need to learn about how to use promises or some other synchronization method.
Consider the output of this code.:
function resolveAfter2Seconds() {
return new Promise(resolve => {
setTimeout(() => {
resolve('resolved');
}, 600);
});
}
var result;
async function asyncCall() {
console.log('calling');
result = await resolveAfter2Seconds();
console.log(result);
// expected output: "resolved"
}
asyncCall().then(() => {
console.log(result)
});
console.log(result);
That is going to quickly log "calling", then undefined, then wait 600 milliseconds and then log "resolved" and another "resolved" quickly.
This part uses promises to coordinate the async function with the parent program.:
asyncCall().then(() => {
console.log(result)
});
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function and https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise explain all this fairly well.
Related
I have this piece of code and I'm struggling to understand why the log order is "1,3,2".
(async () => {
const doSomething = async () => {
console.log("1");
};
await doSomething();
console.log("2");
})();
console.log("3");
Async/await is implemented under the hood with promises. With promises even if a promise resolves immediately the callback within the then is added as a microtask, and the current callstack execution continues. Then once the call stack is completed the microtasks are run.
So the way I read the code, the await keyword before the doSomething() function means that the rest of the code in the function after the await will be wrapped in a then call, giving control back to the caller of the function. Thus "3" should be logged first, so the order should be "3,1,2"?
What is the control flow? What actually happens when the javascript translator comes across an await keyword?
the await keyword before the doSomething() function means that the rest of the code in the function after the await will be wrapped in a then call, giving control back to the caller of the function.
Yes:
(() => {
const doSomething = () => {
console.log("1");
return Promise.resolve();
};
return doSomething().then(() => {
console.log("2");
});
})();
console.log("3");
Thus "3" should be logged first
No. Logging "1" is not delayed by any promise.
this is a problem that is going around for DAYS in my team:
We can't figure out how to save the result of a promise (after .then) in a variable.
Code will explain better what I mean:
We start with a simple async function that retrieves the first item of a list:
const helloWorld = () => [{ name: 'hi' }];
async function getFirstHelloWorld() {
const helloWorldList = await helloWorld();
return helloWorldList[0].name;
}
Now, we would like to call this function and save the content of aList:
let aList;
const a = async() => {
aList = await getFirstHelloWorld()
}
a().then(() => {
console.log('got result:', aList)
console.log('aList is promise:', aList instanceof Promise)
});
console.log(aList)
aList called within the .then(), console.logs the right value.
aList called outside the .then(), console.logs Promise { }
How can we save returned values from promises to a variable?
You cannot take an asynchronously retrieved value, stuff it in a higher scoped variable and then try to use it synchronously. The value will not be present yet because the asynchronous result has not been retrieved yet. You're attempting to use the value in the variable before it has been set.
Please remember that await only suspends execution of a local function, it does not stop the caller from running. At the point you do an await, that function is suspended and the async function immediately returns a promise. So, NO amount of await or return gets you anything by a promise out of an async function. The actual value is NEVER returned directly. All async functions return a promise. The caller of that function then must use await or .then() on that promise to get the value.
Try running this code and pay detailed attention to the order of the log statements.
console.log("1");
const helloWorld = () => [{ name: 'hi' }];
async function getFirstHelloWorld() {
const helloWorldList = await helloWorld();
return helloWorldList[0].name;
}
let aList;
const a = async() => {
console.log("beginning of a()");
aList = await getFirstHelloWorld()
console.log("end of a()");
}
console.log("2");
a().then(() => {
console.log('got result:', aList)
console.log('aList is promise:', aList instanceof Promise)
});
console.log("3");
console.log('attempting to use aList value');
console.log(aList)
console.log("4");
That will give you this output:
1
2
beginning of a()
3
attempting to use aList value
undefined
4
end of a()
got result: hi
aList is promise: false
Here you will notice that you are attempting to use the value of aList BEFORE a() has finished running and set the value. You simply can't do that in Javascript asynchronous code, whether you use await or .then().
And, remember that the return value from an async function becomes the resolved value of the promise that all async functions return. The value is not returned directly - even though the code syntax looks that way - that's a unique property of the way async functions work.
Instead, you MUST use the asynchronous value inside the .then() where you know the value is available or immediately after the await where you know the value is available. If you want to return it back to a caller, then you can return it from an async function, but the caller will have to use .then() or await to get the value out of the promise returned from the async function.
Assigning an asynchronously retrieved value to a higher scoped variable is nearly always a programming error in Javascript because nobody wanting to use that higher scoped variable will have any idea when the value is actually valid. Only code within the promise chain knows when the value is actually there.
Here are some other references on the topic:
Why do I need to await an async function when it is not supposedly returning a Promise?
Will async/await block a thread node.js
How to wait for a JavaScript Promise to resolve before resuming function?
Using resolved promise data synchronously
How to Write Your Code
So, hopefully it is clear that you cannot escape an asynchronous result. It can only be used in asynchronous-aware code. You cannot turn an asynchronously retrieved result into something you can use synchronously and you usually should NOT be stuffing an asynchronous result into a higher scoped variable because that will tempt people writing code in this module to attempt to use the variable BEFORE is is available. So, as such, you have to use the value inside a .then() handler who's resolved value has the value you want or after an await in the same function where the await is. No amount of nesting in more async functions let you escape this!
I've heard some people refer to this as asynchronous poison. Any asynchronous operation/value anywhere in a flow of code makes the entire thing asynchronous.
Here's a simplified version of your code that shows returning the value from an async function which will make it the resolved value of the promise the async function returns and then shows using .then() on that promise to get access to the actual value. It also shows using .catch() to catch any errors (which you shouldn't forget).
FYI, since this is not real asynchronous code (it's just synchronous stuff you've wrapped in some promises), it's hard to tell what the real end code should be. We would need to see where the actual asynchronous operation is rather than just this test code.
const helloWorld = () => [{ name: 'hi' }];
async function getFirstHelloWorld() {
const helloWorldList = await helloWorld();
return helloWorldList[0].name;
}
getFirstHelloWorld().then(name => {
// use the name value here
console.log(name);
}).catch(err => {
console.log(err);
});
You need to return the variable like this and use it within the .then callback.
const a = async() => {
const res = await getFirstHelloWorld()
return res
}
a().then((data) => {
console.log('got result:', data)
});
[Old question] How can you know if your function concumer used await on invoking your function or not:
Maybe some magic like this:
function x(){
return new Promise((resolve, reject)=>{
console.log(x.awaitWasCalled) // true
})
}
const a = await x()
I'm asking about that because I've seen Mongoose (a library) is able to detect weather you've called await or not, HERE
Here is what they're saying:
Mixing promises and callbacks can lead to duplicate entries in arrays.
For example, the below code inserts 2 entries into the tags array,
*not just 1.
const BlogPost = mongoose.model('BlogPost', new Schema({
title: String,
tags: [String]
}));
// Because there's both `await` **and** a callback, this `updateOne()` executes twice
// and thus pushes the same string into `tags` twice.
const update = { $push: { tags: ['javascript'] } };
await BlogPost.updateOne({ title: 'Introduction to Promises' }, update, (err, res) => {
console.log(res);
});
How Mongoose for example was able to detect that I'm using await or not?
[EDIT] ******* After sometime, I've noticed that the answers bellow doesn't actually answer my question.
Please before answering my question: please read their comment above, they're saying: " // Because there's both await and a callback, this updateOne() executes twice"
this means: if you didn't use await, and you passed in a callback, this code will be invoked once, BUT if you used await + callback, this code will be invoked twice, therefore: the question is: how they're able to know if I've used await or not?!
Again: This means, if you didn't use await, this is going to invoke once, and if you used await, this is going to get invoked twice.
You've misunderstood what the documentation is saying.
It says that calling then will execute the query (but that passing a callback will also execute the query, so you shouldn't do both at the same time).
await is essentially alternative syntax for calling then (although it seems to do some magic with caching).
They don't detect await, they just have a method named then.
const notAPromiseButThenable = {
then: function () {
console.log("Then called");
return this;
}
};
(async function () {
await notAPromiseButThenable.then(() => null).then(() => null);
})();
How can I detect that await was mentioned before calling my function?
You can't. In fact, the function is called before await "kicks in". These two are the same:
await myFunction();
let result = myFunction();
await result;
await is not a different way to call a function, it simply operates on Promises, which of course are often returned by functions. I.e. it operates on the return value of a function.
How Mongoose for example was able to detect that I'm using await or not?
Are they actually able to detect that? Nothing in the documentation you linked seems to indicate that.
What they (and you) could do is checking whether a callback is passed or not. If one is passed then they shouldn't return a Promise and vice versa. But I guess that's not how they want to design the API.
This means, if you didn't use await, this is going to invoke once, and if you used await, this is going to get invoked twice.
Yes, but not because they detect anything but because await does something with the return value of the function. That's just how await works. So obviously, if you don't use await then the thing that await would do won't happen ;)
Here is an example:
function someFunction(value, callback) {
// Nothing in here does any "await" detection
let array = [];
if (callback) {
array.push(value);
callback(array);
}
return {
then(resolver) {
array.push(value);
resolver(array);
}
}
}
(async function() {
console.log('with await', await someFunction(42, () => {}));
someFunction(42, array => console.log('without await', array));
}());
My case:
let waiting = function () {
return new Promise(resolve => {
console.log('awaiting...');
setTimeout(function () {
resolve();
}, 1000)
});
};
let waitingAsync = async function () {
console.log('start...');
await waiting();
console.log('stop...');
};
waitingAsync();
console.log('done...');
There are 2 things I don't understand in the code:
The first:
await waiting();
waiting is a synchronous function (because it doesn't have async keyword). So, why can I await a synchronous function?
The second:
Why couldn't done... message be awaited after completing waitingAsync function?
And main question: waitingAsync is an asynchronous function, why is await keyword not required when calling it? Just waitingAsync() instead of await waitingAsync().
If I can await waitingAsync(), done... message would be printed last.
This isn't a function but a value that it returns which is awaited with await statement.
async and normal functions aren't different to the caller. async just returns a promise without returning it explicitly when being called. The result of waitingAsync() call is a promise. The result of waiting() call is a promise, too, so it isn't 'synchronous'.
According to the spec, both promises and non-promises can be awaited. Non-promises are converted to promises with Promise.resolve().
console.log('done...') can't be awaited because it isn't called inside async function. And it doesn't have to be awaited because it doesn't return a promise but undefined. awaiting it would be possible within async function. These await usages are equal and equally useless, all they do is 1 tick delay:
async function ... {
...
await console.log('done...');
}
async function ... {
...
console.log('done...');
await undefined;
}
async function ... {
...
await Promise.resolve(console.log('done...'));
}
A async function does return a promise or, when it uses the await keyword, it must wait for a asynchronous function, being a Promise or another async function. In your code, waiting() is a function that returns a Promise. Then, await waiting() is correct (since it is waiting for a asynchronous function).
An async function is called as another basic functions is called. You may mark a function as async when you desire operate it as an asynchronous function and using the 'await' keyword. The 'done...' is printed because when you call asyncFunction(), it must await until waiting promise is finished. But the program is not stopped and go on showing done.... If you want to wait for it, maybe you can use asyncFunction().then( () => console.log('done...') )
Async keyword used to specify that function will be an instance of AsyncFunction so it will return Promise.
Await is used to wait for a promise resolving inside of async function.
According to a mdn - 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.
When you await a function, if that function returns a promise, its return value will be treated as the promise then value. If the promise will reject, it will be cast to an Error. If the function call returns something ohter than a thenable, well, await then just doesn't nothing.
In the other hand, when you declare an async function, its return value will be returned as a Promise, and any Error thrown from it will be casted to a rejected Promise.
You can use await only within an async declared function.
that's just about async and awaitare, just automatic casting to promises. You don't actually need code using await and async to be really asynchronous (while, well, it's not really useful).
A quick demonstration:
//Will return the string 'Promise' if called through `await`
function getPromise(){
return Promise.resolve('Promise');
}
//Casted to Promise.reject thrught await
function throwError(){
return Promise.reject('error');
}
function get(){
return 'something simple';
}
async function getAsync() {
var response = await getPromise();
return response;
}
//await will cast the rejected Promise to an error
async function getErrorAsync() {
var response = await throwError();
return response;
}
async function simpleGet(){
return get();
}
async function redundantGet(){
return await get();
}
async function catchTheError(){
try{
await throwError();
}
catch(e){
console.log('an error: ' + e );
}
return 'whatever';
}
getAsync().then( console.log ); //Promise
getErrorAsync().catch( console.log ); //error
simpleGet().then( console.log ); //something simple
redundantGet().then( console.log ); //something simple
catchTheError(); //will log 'an error: error'.
So:
waiting is a synchronous function (because it doesn't have async keyword). So, why can I await a synchronous function?
Because you can. The only thing await does is to resolve promise to real values and errors. You don't actually need the function to return a promise.
Why couldn't done... message be awaited after completing waitingAsync function?
async and await only makes your code to behave as it would be synchronous iside asyncdeclared functions. Your last console.log('done') is outside any async function, so it will be just logged before that function ends, since it is asynchronous.
And main question: waitingAsync is an asynchronous function, why is await keyword not required when calling it? Just waitingAsync() instead of await waitingAsync().
Because async keyword casts values to promises -and allows to use await- and nothing more. In fact, since you can only use await inside async functions... you can't expect async functions to be called through await, you would need infinite asyncfunctions :-D.
Before diving in, it's good to notice a few things.
Any reader of the code snippet
let waiting = function () {
return new Promise(resolve => {
console.log('awaiting...');
setTimeout(function () { resolve(); }, 1000);
});
};
let waitingAsync = async function () {
console.log('start...');
await waiting();
console.log('stop...');
};
waitingAsync();
console.log('done...');
may be mislead to believe that the output will be
start...
awaiting...
stop...
done...
while – as you have already noted – done... gets printed before
stop....
The reason is that waitingAsync(); is a call to an asynchronous function,
while console.log('done...'); is just a normal sequential/synchronous
statement that gets carried out right away.
Question 1:
waiting is a synchronous function (because it doesn't have async
keyword) [?]
Answer:
False. The function waiting is asynchronous – it returns
a Promise.
Question 2:
Why couldn't done... message be awaited after completing waitingAsync
function?
Answer:
Because console.log('done...') is not asynchronous.
(It does not return a Promise.)
Question 3:
And main question: waitingAsync is an asynchronous function, why is
await keyword not required when calling it?
Answer:
Well, in your example waitingAsync does not return any value. -
If it would return a value that you care about, then you would need to await
it to get it.
(Hello world! in my Stack Snippet below.)
Question 4:
If I can await waitingAsync(), [the] done... message would be printed
last [?]
Answer:
That depends on what exactly you mean. – See my Stack Snippet below!
As long as the Done! message is printed within the same callback as the call
await waitingAsync(), the answer is Yes!
But if you put console.log('done...?') after the call to the asynchronous
function that encloses await waitingAsync() then the answer is No!
When running the snippet below, pay attention to the order of the output!
Also notice how it takes 1400 ms for Promise resolved! to show up.
function waiting () {
return new Promise(resolve => {
console.log('awaiting...');
setTimeout(function () {
resolve('Hello world!');
console.log('Promise resolved!');
}, 1400);
});
}
async function waitingAsync () {
console.log('start...');
const toBeReturned = await waiting();
console.log('stop...');
return toBeReturned;
}
(async () => {
console.log('Heads up! The next line makes an asynchronous call.');
console.log('Result: ' + await waitingAsync()); // 'Hello world!'
console.log('Done! This will be printed LAST! - Agreed?');
})();
console.log('done...?? This is LAST in the CODE. - I awaited \
"waitingAsync()" above. - So will this be printed at the very end??');
.as-console-wrapper { max-height: 100% !important; top: 0; }
The last asynchronous function is anonymous – without name – and gets
called immediately.
In fact, this is the only function that gets called directly in the snippet.
The function waitingAsync is only called indirectly (by the anonymous
function), and the function waiting is also called indirectly
(by waitingAsync).
A take away lesson
Don't ever put sequential/synchronous code after and outside a call to
an asynchronous function!
You will just confuse yourself if you do. – And even if you don't get
confused, other readers of your code almost certainly will be.
Question:
Is it possible to wait for an async function that starts new async functions?
Details:
I have looked up a bunch of ways to wait for an async function to finish before continuing the code, or running a specific function or code block. But one thing have bugged me for a very long time - I do not know whether new async functions started are also being waited for, or if they require their own piece of code to be taken into account.
Pseudocode:
var value = 1;
af1();
alert(value);
async function af1(){
af2();
}
async function af2(){
af3();
}
async function af3(){
value = 2;
}
I dont know if this is a good example (or even the right syntax), but picture the async functions as some ajax requests that takes some time to finish. I have a feeling that if you add a jQuery deferred on af1, it will only wait for af1 and ignore af2 and af3. I am also using an external javascript file for some of the functions, and I dont really have control over what new functions are started in there.
So again, is it possible to just wrap all of this into something and run some code after all is done? Or am I mistaken about jQuery's deferred and .done functions??
No, async functions are not awaited when called. They just return a promise.
Inside an async function - that's their advantage - you can explicitly await promises, including those that are returned from other async functions.
Your code should have been written using return values, like this:
(async function() { // neccessary to use await
value = await af1();
alert(value);
}());
af1().then(alert); // or just using promise syntax
async function af1(){
return af2();
}
async function af2(){
return af3();
}
async function af3(){
return 2; // or maybe rather something like
return $.ajax(…);
}
But you don't need return values, you can use await as well for your closure approach:
(async function() {
var value = 1;
await af1();
// ^^^^^
alert(value);
async function af1(){
await af2();
}
async function af2(){
await af3();
}
async function af3(){
value = 2; // or maybe rather something like
value = await $.ajax(…);
}
}())
Use this git js ASync
How to use
Async provides around 20 functions that include the usual 'functional' suspects (map, reduce, filter, each…) as well as some common patterns for asynchronous control flow (parallel, series, waterfall…). All these functions assume you follow the Node.js convention of providing a single callback as the last argument of your async function.
Quick Examples
async.map(['file1','file2','file3'], fs.stat, function(err, results){
// results is now an array of stats for each file
});
async.filter(['file1','file2','file3'], fs.exists, function(results){
// results now equals an array of the existing files
});
async.parallel([
function(){ ... },
function(){ ... }
], callback);
async.series([
function(){ ... },
function(){ ... }
]);
There are many more functions available so take a look at the docs below for a full list. This module aims to be comprehensive, so if you feel anything is missing please create a GitHub issue for it.
Read More
Apart from above examples, look at below code sample. concept of async and wait would be more clear.
async function doWork(){
try {
const response = await makeRequest('facebook'); //using await will wait until the response returned from the makeRequest function
//console.log('Response Received' + response );
const response2 = await makeRequest('google');
//console.log('Response2 Received' + response2 );
} catch(err) {
alert(err);
}
}
function makeRequest(str){
//function body that takes time to process, eg: server call
return "making request to " + str;
}
doWork();