How to define a async-Function? - javascript

how can i define a function, that is called asynchronously?
async function getPosts() {
for (i = 0; i < 1000000000; i++){}
console.log("loop done.");
}
console.log("start");
getPosts();
console.log("end");
the result is:
start
loop done.
end
i was expecting:
start
end
loop done
i expect that, because the function "getPosts" is declared as "async".
so the we dont wait until the function is finished.
How do i have to adjust my code to get the expected result?

The async function could be the one calling another function that returns a Promise. Inside the Promise you could put a setTimeout instead of the for loop and finall make the call with asyncCall:
function getPosts() {
return new Promise(resolve => {
setTimeout(() => {
resolve('done.');
}, 2000);
});
}
async function asyncCall() {
var result = await getPosts();
console.log(result);
}
console.log("start");
asyncCall();
console.log("end");

The main reason why your code don't do the "async" thing, it's because of the way js threads works. A FOR loop will use the main thread to do the job soooo, the code will wait it finish de loop. The #shys answer works, but without the loop.
If you want a working async, take a look at the WebWorker api

To sum up what Mauricio Sipmann and shrys correctly told: An asynchronous function is a function which operates asynchronously via the event loop (see async function). In order to continue executing the calling context of your async function getPosts(), i. e. with console.log("end"), the event loop must be given a chance to pause the execution of getPosts(), e. g. with this variant of your function:
function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)) }
async function getPosts() {
for (i = 0; i < 1000; i++) { await sleep(3) } // pause for 3 ms
console.log("loop done.");
}
console.log("start");
getPosts();
console.log("end");
But how is the fetch()-Method implemented?
fetch() likely contains such an await statement, so that the event loop can continue executing while fetch() is waiting for the HTTP response.

Related

How to reset or override a running async loop

I have an async function foo with an inner async iterate that has a loop. The loop awaits an inner promise to resolve before continuing. The outer async awaits iterate to finish before proceeding. I want to implement a reset button which restarts the loop.
Rerunning iterate in the handler, a new parallel running instance of the loop is made which can be run to completion. The issue is that foo is still waiting the first instance to complete. Is there a way to do this properly?
async function foo() {
async function iterate() {
for (let i = 0; i < 5; i++) {
const position = await new Promise() // Resolves after the user does something
// do stuff
}
}
button.onclick = async () => await iterate(); // Set up event handler
await iterate(); // First call
// Button gets clicked -> Second call takes over
// Now first call never ends and can't finish `foo()`
}
I was finally able to do it by using promises and more specifically, calling resolve() after the loop, and more importantly, in the handler:
async function foo() {
async function iterate() {
await new Promise(async (resolve) => {
button.onclick = async () => {
await iterate(); // Set up event handler
resolve();
}
for (let i = 0; i < 5; i++) {
const position = await new Promise()
// do stuff
}
resolve();
}
await iterate();
}
That way, even if initial loop never ends, when the call in the handler finishes, the handler itself calls resolve() and we exit this fooking mess.
This is only mildly hacky, but if someone has a more concise or direct approach, I'd be happy to check it out.

Why does this loop repeat each iteration twice?

The function below prints each number twice. Could someone explain how it works? I tried debugging but all I can see is that the value of i only increases on every second iteration.
async function run(then) {
for (let i = 1; i <= 10; i++) {
console.log(i);
then = await { then };
}
}
run(run);
Concretely speaking, there are two things I don't understand.
Why does i not increase on every single iteration?
What does then = await { then }; exactly do? My first guess was that it would wait for a nested async call to run to finish before moving on to the next iteration, but this does not seem to be the case.
We can make this a bit clearer with minor re-write to include logging:
async function run(callback) {
let then = callback;
for (let i = 1; i <= 10; i++) {
console.log(callback === run ? "A" : "B", i);
then = await { then };
}
}
run(run);
.as-console-wrapper { max-height: 100% !important; }
This shows there are actually two loops started. For simplicity just called A and B. They log and await which means that their logs interleave and lead to A 1, B 1, A 2, B 2, etc.
This happens because of the first statement: run(run). Which passes the same function as a callback to itself. This does not call the callback but it is the first step to unravelling this.
The next step to understanding what is happening is await. You can await any value and in most cases if it's not a promise, it doesn't matter. If you have await 42; it just pretends the value was Promise.resolve(42) and continues the operation immediately with the next tick. That is true for most non-promises. The only exception is thenables - objects which have a .then() method.
When a thenable is awaited, its then() method is called:
const thenable = {
then() {
console.log("called");
}
};
(async () => {
await thenable;
})()
Which then explains the await { then } statement. This uses the shorthand for { then: then } where then is the callback passed to run. Thus it creates a thenable object which, when awaited, will execute the callback.
This means that the first time run() is executed and on the first iteration of the loop A the code is effectively await { then: run } which will execute run again which then starts loop B.
The value of then is overridden each time, hence why it only ever runs two loops in parallel, rather than more.
There is more to thenables that is relevant to fully grasp this code. I showed a simple one before which just shows that awaiting it calls the method. However, in reality await thenable will call .then() with two parameters - functions that can be called for success and failure. In the same way that the Promise constructor does it.
const badThenable = {
then() {
console.log("bad called");
}
};
(async () => {
await badThenable;
console.log("never reached");
})();
const goodThenable = {
then(resolve, reject) { //two callbacks
console.log("good called");
resolve(); //at least one needs to be called
}
};
(async () => {
await goodThenable;
console.log("correctly reached");
})();
This is relevant because run() expects a callback and when the await { then: run } executes it calls run(builtInResolveFunction) which then gets passed to the next await { then: builtInResolveFunction } which in turn resolves causes the a await to resolve.
With all this aside, the interleaved logging is just a factor of how tasks resolve:
(async () => {
for (let i = 1; i <= 10; i++){
console.log("A", i);
await Promise.resolve("just to force a minimal wait");
}
})();
(async () => {
for (let i = 1; i <= 10; i++) {
console.log("B", i);
await Promise.resolve("just to force a minimal wait");
}
})();
If there are two async functions running and there is nothing to really wait for:
One would run until it reaches an await and will then be suspended.
The other would run until it reaches an await and will then be suspended.
Repeat 1. and 2. until there are no more awaits.

Can not make a timer with while loop

I am trying to create a timer with while loops and setTimeout but everytime i give an input the code crashes. İ thought doing it this way would work but seems like it doesnt. What is the problem here? And how do i achieve what i want with while loop and setTimeout?
function countdown(durationInput) {
while (durationInput > 0) {
setTimeout(() => {
console.log(durationInput)
durationInput = durationInput - 1
console.log(durationInput)
}, 1000)
}
}
Well look at this image. What you see is the basics of how javascript works.
JS is single threaded. That means 1 core do the work. Thats the event loop.
The event loop grabs the event from the callback queue and put its on the call stack and executes it.
If you run setTimeout you put it into the callback que
The problem: When the event loop is blocked by an synchronouse task like your while loop, that means the event loop cannot grab the events to the stack, because its blocked.
What happens is: The while loop runs and runs and adds more and more setTimeout events to the callback que until the programm crashes.
1 possible solution could be to use promises:
async function sleep(ms) {
return new Promise(res => {
setTimeout(res, ms)
})
}
async function sleep(ms) {
return new Promise(res => {
setTimeout(res, ms)
})
}
async function main() {
console.log("waiting 5 seconds without blocking the main thread")
await sleep(5000)
console.log("done")
}
main()
The problem is that you are generating an infinite loop, as the durationInput doesn't update until the setTimeout executes. And you are filling the execution stack with a huge cue.
For fixing it, you need to create a variable that will store the initial value of the durationInput and subtract from duration in the while loop.
function countdown(duration, intervalInSeconds = 1) {
let count = duration;
while (duration > 0) {
setTimeout(() => {
console.log(count--);
}, intervalInSeconds * 1000);
duration--;
}
}
countdown(10);

Is this a correct understanding of async/await?

function injectText(value, selector) {
return new Promise((resolve, reject) => {
setTimeout(() => {
document.querySelector(selector).innerHTML = value
resolve()
}, 500)
})
}
async function printWord(word, selector) {
for (let i = 0; i < word.length; i++) {
await injectText(word.substring(0, i + 1), selector)
}
}
async function run() {
await printWord('Hello', '.hello')
await printWord('there!', '.there')
}
run()
<div class="hello"></div>
<div class="there"></div>
I've used Promise and async/await to print Hello there! letter after letter with the delay of 500ms. It works as intended, however, I'm not sure if I understand what happens when function run() executes.
await before printWord means that execution of async funtion run is paused until Promise is resolved or rejected.
Function printWord is an async function. I'm not returning anything from it, therefore undefined is returned at the end of function run. Async functions always return Promises, therefore, it automatically returns Promise which automatically resolves with undefined value? Is this what happens?
Then it jumps to second await printWord where the same logic applies.
Am I understanding it correctly? I appreciate your help.
Yes, the run() function's execution is paused whenever there is an await for it to handle. The function will pause its execution twice before resolving itself.
Yes, an async function does return a Promise; however, realize that promises resolve, and they don't return. At least, in javascript (since async/await is just sugar for Promises) they don't really return, but they resolve.
Yes.

Why `async/await` doesn't work in my case?

I read about async/await, but I've a critical question.
At first I explain an old example to show base of my question and then I ask my exact question.
Everybody know it:
console.log('1');
console.log('2');
console.log('3'); // Ex: 123
It is simple but in below case:
console.log('1');
setTimeout(()=>{
console.log('2');
},0);
console.log('3'); // Ex: 132
It is simple too, setTimeout function is asynchronous and JavaScript jump from it and after resolve run its function, so we see 2 after 1 and 3.
But, now I read async/await and I wrote a function like this:
(async function test() {
console.log('1');
await setTimeout(()=>{
console.log('2');
},0);
console.log('3');
})(); // Ex: 132
The Export is 132 too, why? this is my question, why 3 run before 2? I expect because of async/await after 1 JavaScript wait for 2 and then wrote 3. why 132?
await only suspends when the value passed to it is a Promise. In your case, setTimeout returns a Number so await doesn't wait for it.
The correct code will be as follows:
async function test() {
console.log('1');
await new Promise((resolve, reject) => {
setTimeout(() => {
console.log('2');
resolve()
}, 0);
});
console.log('3');
}
Because setTimeout doesn't return a promise. await x only waits if x is a promise; if x isn't a promise, it's (effectively) wrapped in one as though you had await Promise.resolve(x). That means the code following it will run asynchronously but as soon as possible.*
If you want a promise version of setTimeout, see this question's answers. But even with that, your test function wouldn't use a callback, instead you'd just await the promise-enabled timeout:
function later(delay) {
return new Promise(function(resolve) {
setTimeout(resolve, delay);
});
}
async function test() {
console.log("1");
await later(10);
console.log("2");
console.log("3");
}
test().catch(e => console.error(e));
console.log("After the call (just to prove it does wait for the timeout after 1 and before 2");
* On browsers, that's guaranteed to be before a setTimeout(..., 0) scheduled during the same task, because promise callbacks scheduled during a task occur just after the end of that task, before the next task is picked up from the queue (even if the next task was scheduled before the promise callback). More on this ("macrotasks" and "microtasks") in this question's answers.
You can await to that functions that returns Promise. setTimeout does not returns Promise. So in this case await used before the setTimeout does not has a sense.
You can wrap your setTimeout into a Promise and call resolve at the setTimeout function.
(async function test() {
console.log('1');
await new Promise((resolve, reject) => {
setTimeout(() => {
console.log('2');
resolve(); // also can pass a parameter here to get it via await.
},0);
});
console.log('3');
})();

Categories

Resources