JavaScript async/await and Synchronous JavaScript both look similar and making the program to wait, then what is the use of async/await ?
For example
Synchronous JavaScript Code
function getStudentList() {
return ['Adam', 'Ben'];
}
function findStudent(who) {
const list = getStudentList();
const found = list.some(student => student === who);
console.log(found); // logs true
}
findStudent('Adam');
Asynchronous JavaScript using async/await
function getStudentList() {
return new Promise(resolve => {
setTimeout(() => resolve(['Adam', 'Ben'], 1000);
});
}
async function findStudent(who) {
const list = await getStudentList();
const found = list.some(student => student === who);
console.log(found);
}
findStudent('Adam');
in above code, const list = getStudentList(); and const list = await getStudentList(); both functions like synchronous code.
Thank you
A synchronous function/code is a code that gets executed line by line, task by task. No matter how much time a specific task takes to be executed the next one doesn't start until the current one is finished, in other words the execution flow is paused/stopped in each task and starts back again when the current task is finished. i.e:
console.log("foo");
console.log("bar");
// executing this code will log the following:
// foo
// bar
An asynchronous function/call is a code that starts executed at a certain point in time and then the next tasks get executed even if the first one is not finished. So it gets executed in, say, a parallel timeline or thread if you want to call it.
An example of this would be having a function that logs "ASYNC" after 3 seconds (suppose we have a pre-defined delay function called sleep(seconds)):
async function foo() {
sleep(3); // wait for 3 seconds
console.log("ASYNC");
}
console.log("foo");
foo();
console.log("bar");
// executing this code will log the following:
// foo
// bar
// ASYNC
Note that bar was logged before ASYNC even though the function that logs the latter was called first. Basically asynchronous function don't stop the execution flow and just start executing the code inside it and goes on to the next line of code after the call, regardless of whether the async code was finished or not.
There are times when you actually want the execution flow to stop in asynchronous code. Say you have a variable called x and you want to have a sum operation function called calculate() and its result is stored to x but the function is an online request that is executed with a certain amount of delay (for example, 1 second).
let x = 0;
async main() {
console.log(x);
x = calculate(5,8);
console.log(x);
}
main();
// executing this code will log the following:
// 0
// {}
Note that the value of x didn't become 13 as expected but we got some weird result instead. This is because the request started executing and then the value of x was logged before the request has returned the sum, as a result (and depending on how a the request works) the value of x will be some random object or null/undefined until the request is finished and then x will be equal to 13
To fix this, we have to use the keyword await that stops the asynchronous execution flow and waits for another asynchronous function to end until the flow is back on.
let x = 0;
async main() {
console.log(x);
x = await calculate(5,8);
console.log(x);
}
main();
// executing this code will log the following with a 1 second delay between each log:
// 0
// 13
Side note: the await keyword cannot be used in synchronous context like
await someAsyncFunction();
and is only used before asynchronous functions (the ones that are defined with the async keyword).So await is only used with asynchronous functions in asynchronous contexts like this:
async someAsyncFunction() {
// some tasks
await anotherAsyncFunction();
// some more tasks
}
Your first example is synchronous. It's also faster.
function getStudentList(){
return ['Adam', 'Ben'];
}
function findStudent(who){
const list = getStudentList(), found = list.includes(who);
console.log(found); // logs true
}
console.time('sync'); findStudent('Adam'); console.timeEnd('sync');
Your second example is asynchronous, and slower by about a second. But, you wouldn't be able to test that, unless your async function is resolved first. That's why I used .then.
function getStudentList(){
return new Promise(resolve=>{
setTimeout(() => resolve(['Adam', 'Ben']), 1000);
});
}
async function findStudent(who){
const list = await getStudentList(), found = list.includes(who);
console.log(found);
}
console.time('async');
findStudent('Adam').then(()=>{
console.timeEnd('async');
});
You will notice that arrayInstance.includes is a better solution to find if something is in an Array. By the way, it makes no sense to use async in this particular case.
Here is an async example that makes more sense:
function delay(milliseconds){
return new Promise(resolve=>{
setTimeout(()=>{
resolve(milliseconds);
}, milliseconds);
});
}
async function logicalAsync(){
let wait;
console.time('test');
wait = await delay(1000);
console.timeEnd('test');
console.log('milliseconds = '+wait);
}
logicalAsync();
Of course, synchronous code takes however long it takes to execute, with all the code below it waiting on the above code to be finished processing. Asynchronous code allows the code below to execute (possibly) before the code above it is finished.
Related
According to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all,
two evaluations in the following code runs asynchronously.
var p2 = Promise.all([1337, "hi"]); // non-promise values will be ignored, but the evaluation will be done asynchronously
and a question occurs to me that, in the following example:
async function sleep(x) {
await new Promise((resolve) => setTimeout(resolve, x))
}
async function test(x) {
await sleep(1000);
return x
}
async function run(){
let promises = []
promises.push(test(1))
promises.push(test(2))
let res = await Promise.race(promises)
console.log(res)
}
run()
// I ran it for several times, and it always give me 1
Since these two evaluation ran asynchronously and they consume the same amount of time, is it possible for this program to give me 2 in some cases?
What I dont understand is:
Why this program always gives me 1? If multiple evaluations consume the same amount of time, does javascript promise the earlier one always finish earlier?
Does thread context switching happens in Promise.all()? I've tried another example code of multithreading, and it always gives me 100.
let a = 0
async function test1() {
a++;
}
async function run(){
let promises = []
for (let i=0;i<100;i++){
promises.push(test1())
}
await Promise.all(promises)
console.log(a)
}
run()
// It always give me 100
It seems to never violate atomicity, why?
No.
How Event loop in javascript works is, it will put two timeout callbacks in a "timeout queue" and because the both timeouts have the same timeout delay (1000) their callbacks will be executed in the order of their appearance in the "timeout queue". First "1", then "2".
Why this program always gives me 1? If multiple evaluations consume the same amount of time, does javascript promise the earlier one always finish earlier?
The other answer answered this – the timeouts get resolved in the order they're put into the queue. The timeout deadline for the 2 promise is always necessarily later than the one for 1 promise anyway, since some time will have passed between promises.push(test(1)) and promises.push(test(2)).
The second question:
Does thread context switching happens in Promise.all()? I've tried another example code of multithreading, and it always gives me 100.
No, you didn't try multithreading because there is no multithreading in regular JavaScript.
An async function is synchronous until the first await in it (and sugars its return value to be wrapped in a promise), so
async function test1() {
a++;
// implicit return undefined;
}
is equivalent to
function test1() {
a++;
return Promise.resolve(undefined);
}
This means that
async function run() {
let promises = []
for (let i = 0; i < 100; i++) {
promises.push(test1())
}
await Promise.all(promises)
console.log(a)
}
is equivalent to
async function run() {
let promises = []
for (let i = 0; i < 100; i++) {
a++;
promises.push(Promise.resolve(undefined));
}
await Promise.all(promises);
console.log(a);
}
which is plainly synchronous in how it modifies a.
I have two functions.
1.
async function firstFunction() {
// Do stuff here
// I can't just return doSomeOtherThings because the firstFunction
// returns different data then what doSomeOtherThings does
doSomeOtherThings();
return;
}
async function doSomeOtherThings() {
// do promise stuff here
// this function runs some db operations
}
If I run firstFunction(); will it execute my doSomeOtherThings() function, or will it return early from that and cause some or all of the doSomeOtherThings code to not be executed? Do I need to do await doSomeOtherThings()?
I think there is a bit of confusion here, so I'll try to start from the beggining.
First, async functions always return a promise. If you add another async function inside that, you COULD chain them and wait for a response before returning the first promise. However, if you dont await the inner function, the first function will be resolved while the second is still running.
async function firstFunction() {
if (I want to wait for doSomeOtherThings to finished before ending firstFunction){
await doSomeOtherThings();
} else if (I can finish firstFUnction and let doSomeOtherTHings finish later){
doSomeOtherThings
}
return;
}
async function init() {
const apiResponse = await firstFunction();
};
init();
You need to have await. Actually 'async/await' is just a syntax sugar.
You function updateOtherThings actually returns a Promise, and if you don't await it, then it simply doesn't run.
If you want simply to start and forget it, then write something like:
updateOtherThings().then(() => {});
Background
I am exploring Async/Await syntax, and I thought I had a clever way of waiting on CSS transitions built. It's not working, though, and I can't figure out why.
Code
const setStyleAndWait = async function( el, property, value ) {
var setTheStyle = (el, property, value) =>
new Promise(resolve => {
el.style[property] = value;
const transitionEnded = e => {
if (e.propertyName !== property) return;
el.removeEventListener('transitionend', transitionEnded);
resolve();
}
el.addEventListener('transitionend', transitionEnded);
});
await setTheStyle( this, property, value );
console.log('Run logic inside await\'s parent scope');
};
setStyleAndWait( element, 'opacity', '0' ); // element is a reference to an element with a transition on for it's opacity style
console.log('Run logic outside await\'s parent scope.)
Question
When I run this, the outer console.log runs immediately, while the inner waits until the promise is resolved. Why is the outer logic not waiting for the function to finish?
The setStyleAndWait function is asynchronous and not being awaited, so it returns immediately leading to that outer log finishing first. You can rewrite that as :
setStyleAndWait(element, 'opacity', '0').then(() => console.log('this runs after completion'))
or wrap the entire thing in an async function so you can use await.
why should it wait, when you declare a function with the async keyword, what you are saying is that your function is returning a promise. if you want the function caller to wait for the function to resolve/reject you have to use await or then/catch as you want.
In your code, you just have to add await to setStyleAndWait()
When you add async in front of a function, you are making that function asynchronous, it means that the function is executed but the code jumps to next line not waiting for the resolution.
So in order to make this work you need to encapsulate the entire code inside an async function, and make the constant setStyleAndWait to wait for a value to continue with an await
async function envelope(){
const setStyleAndWait = await function(//rest of code)
}
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();
If I have a function that's passed this function:
function(work) {
work(10);
work(20);
work(30);
}
(There can be any number of work calls with any number in them.)
work performance some asynchronous activity—say, for this example, it just is a timeout. I have full control over what work does on the completion of this operation (and, in fact, its definition in general).
What's the best way of determining when all the calls to work are done?
My current method increments a counter when work is called and decrements it when it completes, and fires the all work done event when the counter is 0 (this is checked after every decrement). However, I worry that this could be a race condition of some sort. If that is not the case, do show my why and that would be a great answer.
There are a ton of ways you can write this program, but your simple technique of using a counter will work just fine.
The important thing to remember, the reason this will work, is because Javascript executes in a single thread. This is true of all browsers and node.js AFAIK.
Based on the thoughtful comments below, the solution works because the JS event loop will execute the functions in an order like:
function(work)
work(10)
counter++
Start async function
work(20)
counter++
Start async function
work(30)
counter++
Start async function
-- back out to event loop --
Async function completes
counter--
-- back out to event loop --
Async function completes
counter--
-- back out to event loop --
Async function completes
counter--
Counter is 0, so you fire your work done message
-- back out to event loop --
There's no race condition. There is the added requirement for every request made to perform a decrement when it's finished (always! including on http failure, which is easy to forget). But that can be handled in a more encapsulated way by wrapping you calls.
Untested, but this is the gist (I've implemented an object instead of a counter, so theoretically you can extend this to have more granular queries about specific requests):
var ajaxWrapper = (function() {
var id = 0, calls = {};
return {
makeRequest: function() {
$.post.apply($, arguments); // for example
calls[id] = true;
return id++;
},
finishRequest: function(id) {
delete calls[id];
},
isAllDone: function(){
var prop;
for(prop in calls) {
if(calls.hasOwnProperty(prop)) {return false;}
}
return true;
}
};
})();
Usage:
Instead of $.post("url", ... function(){ /*success*/ } ... ); We'll do
var requestId;
requestId = ajaxWrapper.makeRequest("url", ...
function(){ /*success*/ ajaxWrapper.finishRequest(requestId); } ... );
If you wanted to be even more sophisticated you could add the calls to finishRequest yourself inside the wrapper, so usage would be almost entirely transparent:
ajaxWrapper.makeRequest("url", ... function(){ /*success*/ } ... );
I have an after utility function.
var after = function _after(count, f) {
var c = 0, results = [];
return function _callback() {
switch (arguments.length) {
case 0: results.push(null); break;
case 1: results.push(arguments[0]); break;
default: results.push(Array.prototype.slice.call(arguments)); break;
}
if (++c === count) {
f.apply(this, results);
}
};
};
The following code below would just work. Because javascript is single threaded.
function doWork(work) {
work(10);
work(20);
work(30);
}
WorkHandler(doWork);
function WorkHandler(cb) {
var counter = 0,
finish;
cb(function _work(item) {
counter++;
// somethingAsync calls `finish` when it's finished
somethingAsync(item, function _cb() {
finish()
});
});
finish = after(counter, function() {
console.log('work finished');
});
};
I guess I should explain.
We pass the function that does work to the workhandler.
The work handler calls it and passes in work.
The function that does work calls work multiple times incrementing the counter
Since the function that does work is not asynchronous (very important) we can define the finish function after it has finished.
The asynchronouswork that is being done cannot finish (and call the undefined finish function) before the current synchronous block of work (the execution of the entire workhandler) has finished.
This means that after the entire workhandler has finished (and the variable finish is set) the asynchronous work jobs will start to end and call finish. Only once all of them have called finish will the callback send to after fire.