I am new to asynchronous code in general (and JavaScript in particular) and have been falling prey to common pitfalls, so I want to re-evaluate my most basic assumptions.
Testing the code below does not give me an answer since, just because the test passed, doesn't mean the code will run the same way in the future.
The question is very general, so I will give a couple concrete examples of the basics I am trying to understand.
First Example
const secret_key = "BLAH BLAH";
var url = `https://www.google.com/recaptcha/api/siteverify?secret=${secret_key}&response=${token}`;
If the above code occurs in an async function, can I be sure that url will be defined correctly (i.e. that the second statement will occur after the first)? Does this change if secret_key is a var? Or let?
Second Example
blah = async function() {
syncFunction();
}
Can I be certain that the synchronous function will be run to completion before the function returns (even if it has to do some time-intensive work)?
I ask because my understanding is that this would not be true for an asynchronous function (as explained here) unless I use await / promises.
Update
In the end, this Stack Overflow question resolved my problem. Basically I was using await, but the function being called had an undefined Promise.
First example
can I be sure that url will be defined correctly (i.e. that the second
statement will occur after the first)?
Why yes, absolutely. These are just two regular lines of codes, you simply store two strings in two variables; of course they will be executed in order, there's nothing asynchronous here.
Second example
Using Typescript instead of Javascript helped me a lot to understand how async functions work. Here's your example in Typescript :
const syncFunction = ():string => {
return "hello"
}
const blah = async ():Promise<string> => {
return syncFunction()
}
(async () => {
const sayHello:string = await blah();
console.log(sayHello) // "hello"
})()
As you can see, adding the types helps.
An async function always returns a Promise. If you return true, then the returned type isn't boolean, it is Promise<boolean>.
So, return syncFunction() returns a Promise of "hello". Therefore, you must await it to actually get your "hello" string. Then you can log it.
Same code transpiled to JS so it is runnable in a snippet :
const syncFunction = () => {
return "hello"
}
const blah = async () => {
return syncFunction()
}
(async () => {
const sayHello = await blah();
console.log(sayHello) // "hello"
})()
Related
I am aware of the questions JavaScript wait for asynchronous function in if statement [duplicate] and How do I return the response from an asynchronous call?, but (maybe because I am an absolute JS beginner) I am not able to implement those ideas.
Using async await documentation, I can make this example work:
let hello = async () => { return "Hello" };
hello().then((value) => console.log(value))
It just outputs Hello. However, I would like my code to check what the function hello returns at some later point. Something like this:
if ( /* what comes here? */) {
console.log("Hello was said")
}
How could I do this? How to formulate this if statement to check if hello indeed returned "Hello"?
hello().then((value) => ...) basically means that if the promise (await) is fulfilled, it should call a function(value = <return_value>){} ... so...
hello().then((value) => {
if(value == 'Hello'){
console.log("Hello was said");
}
});
async/Promise introduces an independent execution that would block otherwise
"Later" happens at the completion of its blocking thread
The callback you attach to .then() gets executed at that "later" time
Here is an example that may clear up some of the fog.
Note the two independent threads after setFree() is called.
gone() has gone on to do its own thing
setFree() returns immediately.
A common variable waitOver is shared between the two executions
The then() handler "knows" and takes care of things at that "later" time
That handler updates the common variable waitOver to inform those interested in knowing what happens at that "later" time
"
let waitOver = false;
let whatToDo = setFree("Jill",5);
console.log(whatToDo);
let theWait = setInterval( () => {
waitOver ? clearInterval(theWait) :
console.log(" and .. wait");
}, 1000);
function gone(secs) {
return new Promise(resolve => setTimeout(resolve, secs*1000));
}
function setFree(someone,secs) {
console.log(`If you truly love ${someone}, set ${someone} free`);
gone(secs).then(() => {
console.log(`Back! ${someone} is yours`);
waitOver = true;
return `${someone} is yours`;
}).catch(`Hunting ${someone} down and kill ${someone}`);
return " Here you wait ... ";
}
This is what you'll get:
If you truly love Jill, set Jill free
Here I wait ...
and .. wait
and .. wait
and .. wait
and .. wait
Back! Jill is yours*
NOTICE ALSO that after calling setFree(), any check on waitOver in the main execution thread would be false unless that check is made after gone() has been handled by its .then() handler.
If there is no .then() handler then gone() would have gone to the woods, and you will never know if Jill ever came back, even if she did because that completion was never handled :-)
It depend on what you mean for "Later".
you can save it as a regular variable and export it(it is not a matter of async-await) Export Variables.
and if you want to do it in a safe way and "later" is someday after you need to send it to a DB.
The if statement can be wrapped in an async function like this:
let hello = async () => { return "Hello" };
async function foobar() {
if(await hello() == 'Hello')
console.log('yay')
}
foobar()
await can only be used in async functions.
Promises continue giving you promises as a return, but await can evaluate a Promise in an async function.
[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));
}());
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
(7 answers)
Closed 2 years ago.
I am getting into Node, and being thrown into the world of async programming.
I thoroughly read this article on Mozilla's site (along with many other links teaching about the async and await keywords):
https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await
I have a project I am starting that will require numerous database calls, one after another. Some queries will rely upon the previous one's results. That being said, I decided I better practice and write some testing code.
Here is my testing code:
var users, items;
const getData = async function(){
// This is a DB call. Will take
// a little bit of time to complete.
setTimeout(() => {
var queryResults = "A list of items";
items = queryResults;
}, 1000);
return items;
}
const getUsers = async function(){
setTimeout(() => {
var queryResults = "A list of items";
users = queryResults;
}, 1000);
return users;
}
const init = async function(){
var itemSuccess = await getData();
var userSuccess = await getUsers();
console.log(`Here is your items: ${items}, and here are your users: ${users}`);
}
init();
My code failed with:
Here is your items: undefined, and here are your users: undefined
So naturally I headed back to Mozilla. I re-read the docs, and tried a few changes. Still, no success. Ultimately I ended up back at my original code above.
I cannot see why this is failing. I am simulating the time a query would take by using a timeout. If async does what it says, both functions should return their value, and then my console log should print out the correct results.
Does anyone see where I am going wrong?
Thank you.
Show us your REAL code with the actual database calls in it and we can help you much more specifically, including showing you working code. Please resist the temptation to post questions with pseudo-code. Show real code and we can help you so much faster and better if you show us the real code.
await ONLY does something useful when you await a promise that resolves or rejects when something you are interested in completes or errors.
It has no magic to somehow know when a a setTimeout() or any other asynchronous operation is done doing whatever it does.
So, when you do this:
var itemSuccess = await getData();
And, getData() looks like this:
const getData = async function(){
// This is a DB call. Will take
// a little bit of time to complete.
setTimeout(() => {
var queryResults = "A list of items";
items = queryResults;
}, 1000);
return items;
}
All you end up doing is:
var itemSuccess = await undefined
because your getData() function doesn't return a promise and, in fact, it doesn't even return the items because it returns BEFORE your timer so items (which doesn't appear to be declared properly here) has not yet received its value.
So, I would suggest you start back with the basics and go read what await really does. Make sure you understand how it interacts with a promise since it's only useful when used with a promise. And, then learn how to make your asynchronous operations return promises that are connected to when your asynchronous operations complete. Then, and only then, can you make proper use of await.
In a well organized world, you make sure that all your asynchronous operations are already returned promises and then you can async/await and .then() and .catch() to control the flow of all your asynchronous operations. In many cases, this involves learning the promise interface for whatever you're using. For example, modern versions of node.js have an entire promise interface for the file system in fs.promises.
In a few cases, there may not exist a promise interface for some library you're using and you may have to wrap the asynchronous operation in a promise to make your own promise interface. util.promisify() is built-into node.js to help you do that.
If you show the ACTUAL code, include which database you're using and the actual database call you're trying to make, then someone here can probably help you with the proper way to use the promise interface on the database. This pseudo code prevents us from helping you that specifically. Without your actual code and knowing the actual DB calls you're making in a specific DB, it's hard for us to advise much more than go use the promise interface to your database and learn how to use those promises properly. Happy to help more specifically, but we need to see you REAL code to do that.
And, lastly in Javascript if you're assigning a value to something in a higher scope from within any asynchronous callback, there's a 99.99% chance you're doing things wrong. The only place you can actually use that value is inside the callback itself or in a function that you call from there and pass the value to. So, when you do items = queryResults, where items is declared in a couple scopes above you, then alarm bells should go off that this is not going to work for you. It's not how you program with asynchronous code in Javascript, not just for style reasons, but because it doesn't work.
Use:
function timeout(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
instead of
setTimeout(() => {
}, 1000);
await waits for a Promise to resolve.
// normal function that returns a promise
const getData = function(){
// This is a DB call. Will take
// a little bit of time to complete.
return new Promise(resolve => {
setTimeout(() => {
var queryResults = "A list of data";
resolve(queryResults);
}, 1000);
});
}
const getUsers = function(){
return new Promise(resolve => {
setTimeout(() => {
var queryResults = "A list of users";
resolve(queryResults);
}, 1000);
});
}
const init = async function(){
var itemSuccess = await getData();
var userSuccess = await getUsers();
console.log(`Here is your items: ${itemSuccess}, and here are your users: ${userSuccess}`);
}
init();
An async function does 2 things.
it makes it possible to use the await keyword to wait for promises.
it makes the function automatically return a promise itself that wraps whatever the function returns
async function foo() {
return 123;
}
const v = foo();
console.log(v instanceof Promise);
v.then(value => console.log(value));
A more normal database query might look something like this
async function dbQuery(queryUrl) {
const res = await fetch(queryUrl); // fetch returns a promise that resolves to a Response
return res.json(); // res.json returns a promise that resolves to the body of response parsed as json.
}
async function main() {
const films = await dbQuery('https://greggman.github.io/doodles/test/data/films.json');
console.log(JSON.stringify(result, null, 2));
}
main();
I moved to async/await from promises lately. However, I'm uncertain about the try...catch right flow. I have the following example:
const rp = require('request-promise');
const func2 = async () => {
const response1 = await rp('http://nodejs.org/dist/index.json');
if (response1) {
return await rp('http://headers.jsontest.com'); // #opt 1: return a value
// return rp('http://headers.jsontest.com'); // #opt 2: return a promise:
}
}
(func1 = async () => {
try {
const response = await func2();
console.log('Success!');
} catch(e) {
console.log('Failed!');
}
})();
I was wondering if I should add try...catch to func2, but I came to conclusion it's already being handled in the try...catch of func1.
Is this code valid?
Is this code valid?
Yes. (In terms of the try/catch aspect, see "side note" below.)
I was wondering if I should add try...catch to func2, but I came to conclusion it's already being handled in the try...catch of func1.
Yes, it is. Just like with non-async functions, you only need the try/catch in func2 if you want to handle the error there (entirely, or partially and then (re)throwing). If you don't, allow it to propagate to the caller. Just ensure it's handled somewhere.
Side note: I'm assuming your func2 has a return in the case where response1 is a falsy value. If not, func2's promise's resolution value will be undefined in that case.
Side note 2: I'm guessing you've added the func1 = part there just to give yourself a name to refer to in the question and it's not in your real code. But in case it is: Unless you've declared it somewhere you haven't shown, in your example func1 is an implicit global. Recommend avoiding those. :-) More in my blog post: The Horror of Implicit Globals
Side note 3 (he does go on a bit, doesn't he?): If you're not using func1 anywhere else (just using it as a wrapper for convenient await use within it), if you want to, you can avoid a level of indentation by using the fact that async functions return promises:
(async () => {
const response = await func2();
console.log('Success!');
})().catch(e => {
console.log('Failed!');
});
The up side is that it's a bit more concise. The downside is that it mixes metaphors. :-) That and the try/catch do exactly the same thing, it's purely a matter of how you prefer to write it, which is entirely up to you.
I am confused about the current discussion of adding async functions and the keyword await to the next EcmaScript.
I do not understand why it is necessary to have the async keyword before the function keyword.
From my point of view the await keyword to wait for a result of a generator or promise done, a function's return should be enough.
await should simple be usable within normal functions and generator functions with no additional async marker.
And if I need to create a function what should be usable as an result for an await, I simply use a promise.
My reason for asking is this good explanation, where the following example comes from:
async function setupNewUser(name) {
var invitations,
newUser = await createUser(name),
friends = await getFacebookFriends(name);
if (friends) {
invitations = await inviteFacebookFriends(friends);
}
// some more logic
}
It also could be done as normal function, if the execution of a function will wait for finishing the hole function until all awaits are fulfilled.
function setupNewUser(name) {
var invitations,
newUser = await createUser(name),
friends = await getFacebookFriends(name);
if (friends) {
invitations = await inviteFacebookFriends(friends);
}
// return because createUser() and getFacebookFriends() and maybe inviteFacebookFriends() finished their awaited result.
}
In my opinion the whole function execution is holding until the next tick (await fulfillment) is done. The difference to Generator-Function is that the next() is triggering and changing the object's value and done field. A function instead will simple give back the result when it is done and the trigger is a function internal trigger like a while-loop.
I do not understand why it is necessary to have the async keyword before the function keyword.
For the same reason that we have the * symbol before generator functions: They mark the function as extraordinary. They are quite similar in that regard - they add a visual marker that the body of this function does not run to completion by itself, but can be interleaved arbitrarily with other code.
The * denotes a generator function, which will always return a generator that can be advanced (and stopped) from outside by consuming it similar to an iterator.
The async denotes an asynchronous function, which will always return a promise that depends on other promises and whose execution is concurrent to other asynchronous operations (and might be cancelled from outside).
It's true that the keyword is not strictly necessary and the kind of the function could be determined by whether the respective keywords (yield(*)/await) appear in its body, but that would lead to less maintainable code:
less comprehensible, because you need to scan the whole body to determine the kind
more errorprone, because it's easy to break a function by adding/removing those keywords without getting a syntax error
a normal function, whose execution will wait for finishing the hole body until all awaits are fulfilled
That sounds like you want a blocking function, which is a very bad idea in a concurrent setting.
By marking a function as async, you're telling JS to always return a Promise.
Because it will always return a Promise, it can also await on promises inside of its own block. Imagine it like one giant Promise chain - what happens internally to the function gets effectively gets bolted on to its internal .then() block, and what's returned is the final .then() in the chain.
For example, this function...
async function test() {
return 'hello world';
}
... returns a Promise. So you can execute it like one, .then() and all.
test().then(message => {
// message = 'hello world'
});
So...
async function test() {
const user = await getUser();
const report = await user.getReport();
report.read = true
return report;
}
Is roughly analogous to...
function test() {
return getUser().then(function (user) {
return user.getReport().then(function (report) {
report.read = true;
return report;
});
});
}
In both cases, the callback passed to test().then() will receive report as its first parameter.
Generators (i.e. marking a function * and using the yield keyword) are a different concept altogether. They don't use Promises. They effectively allow you to 'jump' between different portions of your code, yielding a result from inside of a function and then jumping back to that point and resuming for the next yield block.
Although they feel somewhat similar (i.e. 'halting' execution until something happens somewhere else), async/await only gives you that illusion because it messes with the internal ordering of Promise execution. It's not actually waiting - it's just shuffling when the callbacks happen.
Generators, by contrast, are implemented differently so that the generator can maintain state and be iterated over. Again, nothing to do with Promises.
The line is further blurred because at the current time of writing, support for async/await is scare; Chakracore supports it natively, and V8 has it coming soon. In the meantime, transpilers like Babel allow you to write async/await and convert the code to generators. It's a mistake to conclude that generators and async/await are therefore the same; they're not... it just so happens that you can bastardize how yield works alongside Promises to get a similar result.
Update: November 2017
Node LTS now has native async/await support, so you ought never to need to use generators to simulate Promises.
These answers all give valid arguments for why the async keyword is a good thing, but none of them actually mentions the real reason why it had to be added to the spec.
The reason is that this was a valid JS pre-ES7
function await(x) {
return 'awaiting ' + x
}
function foo() {
return(await(42))
}
According to your logic, would foo() return Promise{42} or "awaiting 42"? (returning a Promise would break backward compatibility)
So the answer is: await is a regular identifier and it's only treated as a keyword inside async functions, so they have to be marked in some way.
The reason for the async keyword in front is simple so you know that return value will be transformed into a promise. If no keyword how would the Interpreter know to do this.
I think this was first introduce in C# and EcmaScript is taking a loot of stuff from TypeScript. TypeScript and C# are conceived by Anders Hejlsberg and are similar.
lets say you have a function (this one is just to have some asynchronous work)
function timeoutPromise() {
return (new Promise(function(resolve, reject) {
var random = Math.random()*1000;
setTimeout(
function() {
resolve(random);
}, random);
}));
}
this function will make us wait for a random time and return a Promise (if you use jQuery Promise is similar to Deferred) object. To use this function today you would write something like this
function test(){
timeoutPromise().then(function(waited){
console.log('I waited' + waited);
});
}
And this is fine. Now lets try to return the log message
function test(){
return timeoutPromise().then(function(waited){
var message = 'I waited' + waited;
console.log(message);
return message; //this is where jQuery Deferred is different then a Promise and better in my opinion
});
}
Ok this is not bad but there are two return statements and a function in the code.
Now with async this will look like this
async function test(){
var message = 'I waited' + (await timeoutPromise());
console.log(message);
return message;
}
The code is short and inline. If you written a lot of .then() or . done() you know how unreadable the code can get.
Now why the async keyword in front of function. Well this is to indicate that your return value is not what is returned. In theory you could write this(This can be done in c# I don't know if js will allow since it's not done).
async function test(wait){
if(wait == true){
return await timeoutPromise();
}
return 5;
}
you see, you return a number but the actual return will be a Promise you don't have to use
return new Promise(function(resolve, reject) { resolve(5);};
Since you can't await a number only a Promise await test(false) will throw an exception and await test(true) will not if you don't indicate async in front.