New to js,I want to output 5 '!'s with a 1-second interval,and finally output "end".
I thought the problem was related to asynchronization.
I have tried a lot of times and a lot of methods such as "await,async" and "promise" ,but still failed.
class A {
constructor() {
this.cnt = 5;
}
real() {
this.cnt--;
console.log("!");
}
getCnt() {
return this.cnt;
}
}
class B {
constructor() {
this.a = new A();
}
fun() {
if (this.a.getCnt() > 0) {
this.a.real();
setTimeout(() => this.fun(), 1000);
}
}
}
class C {
constructor() {
this.b = new B();
}
f() {
this.b.fun();
console.log("end");
}
}
var c = new C();
c.f();
Skipping the complexity of the 3 classes involved, this is elegantly solved with async functions. (Without async functions, the cascade of setTimeouts in a loop becomes more difficult to manage.)
This can of course be wrapped into a trio of classes if required.
// Turns `setTimeout` into a promise you can `await`.
async function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
// Calls a function `fun` `times` times, delaying for `delayTime` ms between each invocation.
// Since this is an async function, it returns a promise that will resolve as soon as it's done,
// which in turn can be awaited upon.
async function repeatWithDelay(fun, times, delayTime) {
for (let i = 0; i < times; i++) {
await fun(i);
await delay(delayTime);
}
}
// Prints the number passed in (could just print an exclamation mark).
function print(i) {
console.log(`${i}!`);
}
async function main() {
console.log("Go!");
await repeatWithDelay(print, 5, 500);
console.log("Done!");
}
// Assumes top-level `await` is not available in your environment.
// If it is, this too can be replaced with a simple `await`.
main().then(() => {
console.log("Main done.");
});
This prints out
Go!
0!
1!
2!
3!
4!
Done!
Main done.
Ultimately you need to make your method fun be able to report when it is finished as it is an asynchronous method (calls setTimeout). The best way to do this is return a Promise which allows calls to use await on it
fun() {
return new Promise(resolve => {
const exec = () => {
if (this.a.getCnt() > 0) {
this.a.real();
setTimeout(exec.bind(this), 1000);
}
else{
resolve();
}
}
exec.apply(this);
})
}
Once you've done that, also mark function f as async which allows you to call await inside it:
async f() {
await this.b.fun();
console.log("end");
}
And then all works as expected:
class A {
constructor() {
this.cnt = 5;
}
real() {
this.cnt--;
console.log("!");
}
getCnt() {
return this.cnt;
}
}
class B {
constructor() {
this.a = new A();
}
fun() {
return new Promise(resolve => {
const exec = () => {
if (this.a.getCnt() > 0) {
this.a.real();
setTimeout(exec.bind(this), 1000);
}
else{
resolve();
}
}
exec.apply(this);
})
}
}
class C {
constructor() {
this.b = new B();
}
async f() {
await this.b.fun();
console.log("end");
}
}
var c = new C();
c.f();
I tried to write a generic function that emulate typing, hope this help you understand async/await functions
let fakeConsole = document.getElementById('console');
async function sleep(time) {
await new Promise(r => setTimeout(r, time));
}
function type(text) {
fakeConsole.innerHTML += text;
}
async function emulateTyping(text, speed) {
// split the text into an array of characters;
const characters = text.split('').reverse();
if (characters.length > 0) {
// display first character right away
type(characters.pop());
while (characters.length > 0) {
// wait <speed> millisections
await sleep(speed);
// type one character
type(characters.pop());
}
}
}
async function main() {
// async function we wait for the end with "await"
await emulateTyping("!!!!!", 500);
// sync function
type(' END');
};
main();
<pre id="console"></pre>
If using promise, then you need to resolve the response in setTimeout too
class A {
constructor() {
this.cnt = 5;
}
real() {
this.cnt--;
console.log("!");
}
getCnt() {
return this.cnt;
}
}
class B {
constructor() {
this.a = new A();
}
fun() {
return new Promise((resolve) => {
if (this.a.getCnt() > 0) {
this.a.real();
setTimeout(() => resolve(this.fun()), 1000);
} else {
resolve()
}
})
}
}
class C {
constructor() {
this.b = new B();
}
f() {
this.b.fun().then(() => {
console.log("end");
})
}
}
var c = new C();
c.f();
For multiple loops:
var loop = [array of 100 items];
await Promise.all(
loop.map(async (item) => await this.b.fun();)
)
console.log("end");
Related
I'm trying to show the process of how the backtracking algorithm solves a sudoku board, but I'm not sure how I can make sure that it only gets called every 500ms.
function solveBoard(board) {
// I tried doing setTimeout from here to the bottom, but it breaks the solver and just puts 9s everywhere.
let empty = findEmpty(board);
if (!empty) return true;
let row = empty[0];
let col = empty[1];
for (let i = 1; i < 10; i++) {
board[row][col] = i;
console.log(board[row][col]);
document.getElementById(`${row}-${col}`).value = i;
if (checkValid(board, row, col)) {
if (solveBoard(board)) {
return true;
}
}
board[row][col] = 0;
}
return false;
}
The first time I call solve board is just an event listener.
solveBtn.addEventListener("click", () => {
solveBoard(boardArray);
});
Call sleep in solveBoard
async function solveBoard(board) {
await sleep()
// …
}
function sleep(ms = 500) {
return new Promise(resolve => setTimeout(resolve, ms))
}
// Demo
(async () => {
console.log('a')
await sleep()
console.log('b')
await sleep()
console.log('c')
})();
my function greetings works but when i try to return a promise to execute things once its done im getting nothing, what am i missing?
I have tried putting return resolve() as to make sure the function ends but still nothing, I can´t get the .then() to execute.
const greetingDivs = [firstDiv, secondDiv, thirdDiv, fourthDiv, fifthDiv]
let y = 0
function greetings() {
return new Promise((resolve, reject) => {
if (y == greetingDivs.length) {
// console.log('resolved')
resolve('done')
}
if (y != greetingDivs.length) {
setTimeout(() => {
let lastDiv = consoleOutput.appendChild(greetingDivs[y])
.scrollIntoView(false, { behavior: 'smooth' })
y++
greetings()
}, 300)
}
})
}
greetings().then(() => {
console.log('hello')
})
Your code only resolves one promise, while it creates 5. Notably, the first one, the one that greetings() returns, never resolves.
I would suggest promisifying setTimeout, so you have that logic once and for all. Then the looping can be done in an async function with a for loop and await:
const consoleOutput = document.body;
const [firstDiv, secondDiv, thirdDiv, fourthDiv, fifthDiv] = [
"Hello", "Welcome to this demo", "Hope it suits your case", "Test it", "and enjoy"].map(s => {
const div = document.createElement("div");
div.textContent = s;
return div;
});
const greetingDivs = [firstDiv, secondDiv, thirdDiv, fourthDiv, fifthDiv];
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
async function greetings() {
for (let div of greetingDivs) {
consoleOutput.appendChild(div)
.scrollIntoView(false, { behavior: 'smooth' });
await delay(300);
}
}
greetings().then(() => {
console.log('hello');
});
See ES6 promise for loop for more ideas on how you can create such loops.
can someone try to fix my problem?
I am trying to get this output:
100x do C
done B
done A
But everytime i get this one:
10x do C
done B
done A
Is it possible to get it without using async/await feature?
function a() {
let myPromise = new Promise(function(myResolve, myReject) {
b();
myResolve()
});
myPromise.then(function() {
console.log('done A')
});
}
function b() {
let myPromise = new Promise(function(myResolve, myReject) {
for (i = 0; i < 10; i++) {
c();
}
myResolve()
});
myPromise.then(function() {
console.log('done B')
});
}
function c() {
for (i = 0; i < 10; i++) {
console.log('do C')
}
}
a()
The problem is that you're not declaring i, so you're creating an implicit global variable that both b and c write to. (I wrote an article about these years ago: The Horror of Implicit Globals.)
In the below, I'm going to make it three runs of three instead of ten runs of ten, just so we can see it more clearly (and so I don't have to increase the size of the in-snippet console's history — it has an option for that, but I don't recall the details).
I strongly recommend using strict mode so that this is the error it always should have been. Here's your code in strict mode and with rejection handlers added:
"use strict";
function a() {
let myPromise = new Promise(function(myResolve, myReject) {
b();
myResolve()
});
myPromise.then(function() {
console.log('done A')
}).catch(function(err) {
console.error(err);
});
}
function b() {
let myPromise = new Promise(function(myResolve, myReject) {
for (i = 0; i < 3; i++) {
c();
}
myResolve()
});
myPromise.then(function() {
console.log('done B')
}).catch(function(err) {
console.error(err);
});
}
function c() {
for (i = 0; i < 3; i++) {
console.log('do C')
}
}
a()
If you declare i (in both places), you'll get the result you expect.
function a() {
let myPromise = new Promise(function(myResolve, myReject) {
b();
myResolve()
});
myPromise.then(function() {
console.log('done A')
});
}
function b() {
let myPromise = new Promise(function(myResolve, myReject) {
for (let i = 0; i < 3; i++) { // ***
c();
}
myResolve()
});
myPromise.then(function() {
console.log('done B')
});
}
function c() {
for (let i = 0; i < 3; i++) { // ***
console.log('do C')
}
}
a()
but, beware that the reason you're getting the output you're getting is that everything except the calls to the fulfillment handlers is synchronous. If instead of a for loop you were doing some asynchronous process, there's nothing preventing you from getting done B or done A before all of the work done by c is done, because there's nothing connecting those promises together.
Let's see that. Suppose we have an asynchronous version of console.log that waits 10ms before writing the log entry (but we'll use console.log to write out done A and done B immediately, so you can see when they ran):
function asyncLog(...msgs) {
return new Promise(resolve => {
setTimeout(() => {
console.log(...msgs);
resolve();
}, 10);
});
}
Let's look at what happens:
function asyncLog(...msgs) {
return new Promise(resolve => {
setTimeout(() => {
console.log(...msgs);
resolve();
}, 10);
});
}
function a() {
let myPromise = new Promise(function(myResolve, myReject) {
b();
myResolve();
});
myPromise.then(function() {
console.log('done A');
});
}
function b() {
let myPromise = new Promise(function(myResolve, myReject) {
for (let i = 0; i < 3; i++) {
c();
}
myResolve()
});
myPromise.then(function() {
console.log('done B')
});
}
function c() {
for (let i = 0; i < 3; i++) {
asyncLog('do C');
}
}
a();
Notice how things aren't connected to each other.
To connect them, you'd have to make b wait for c's promise to be fulfilled, and make a wait for b's promise to be fulfilled. Something like this:
function asyncLog(...msgs) {
return new Promise(resolve => {
setTimeout(() => {
console.log(...msgs);
resolve();
}, 10);
});
}
function a() {
let myPromise = new Promise((resolve, reject) => {
resolve(b());
});
return myPromise
.then(() => {
console.log('done A');
});
}
function b() {
let myPromise = Promise.resolve();
for (let i = 0; i < 3; i++) {
myPromise = myPromise.then(c);
}
return myPromise
.then(() => {
console.log('done B')
});
}
function c() {
let p = Promise.resolve();
for (let i = 0; i < 3; i++) {
p = p.then(() => asyncLog('do C'));
}
return p;
}
a()
.then(() => {
console.log("all done");
})
.catch(error => {
console.error(`Error: ${error.message}`);
});
I want to execute some code after a certain delay in a loop. And once all the iterations are done, I want to do some other tasks. These depend on the results obtained from task1. For this I have written the following code snippet using generator, async/await and promise:
function* iter() {
for (var i = 0; i < 10; i++) yield i
}
async function start() {
var myIter = iter();
var p = await cb1(myIter);
console.log('after await');
p.then((value) => {
console.log('-----------here-------------');
});
}
start();
function cb1(myIter) {
console.log("Started : " + new Date());
var obj;
return new Promise((resolve, reject) => {
setTimeout(function(){
if(myIter.next().done === true) {
console.log("End : " + new Date());
resolve('done');
}else {
console.log("---in else---");
cb1(myIter);
}
}, 3000);
});
}
The issue is, the console in p.then() never gets printed. This means that the promise never gets resolved and the program terminates. The iterations execute as expected, but the promise never resolves. What could be wrong here? I am using recursion to trigger iterator.next() and want to resolve the promise only on the last iteration, that is when done=true.
Scratching my head since long on this issue. Help is appreciated. Following is the output of this program.
A couple problems: the line
var p = await cb1(myIter);
results in p being assigned the value of the result of calling cb1. p is not a Promise unless the constructed Promise resolves to a Promise as well, which is unusual. Because await essentially pauses the execution of the script until the promise resolves, you don't need .then - you just need to add the console.log below in the start function. Once you chain the promises together properly, p will resolve to a string of 'done', which of course isn't a Promise.
But there's another problem: your promises returned by cb1 never resolve, except at the very end, where yuo're calling resolve. In the else, you're not ever calling resolve, so those promises remains unresolved forever. To fix this, change
} else {
console.log("---in else---");
cb1(myIter);
}
to
} else {
console.log("---in else---");
cb1(myIter).then(resolve);
}
so that the current iteration's Promise resolves once the next iteration's Promise resolves.
function* iter() {
for (var i = 0; i < 3; i++) yield i
}
async function start() {
var myIter = iter();
var p = await cb1(myIter);
console.log('after await');
console.log('-----------here-------------');
}
start();
function cb1(myIter) {
console.log("Started : " + new Date());
var obj;
return new Promise((resolve, reject) => {
setTimeout(function() {
if (myIter.next().done === true) {
console.log("End : " + new Date());
resolve('done');
} else {
console.log("---in else---");
cb1(myIter).then(resolve);
}
}, 1000);
});
}
There are a couple of issues:
The result of await will never be a promise; the purpose of await is to wait for the promise to resolve and give you the resolution value. So p in your code isn't a promise, and won't have a then method. But you're not getting an error about that because of #2.
Each call to cb1 creates a new promise, with a new resolve function. Your last setTimeout callback is resolving the last promise, but nothing ever resolves the first one, so you never get past that var p = await cb1(myIter); line.
You probably want an inner function for the timer callback, and then have the promise returned by the cb1 call resolve.
Something along these lines:
function* iter() {
for (var i = 0; i < 10; i++) {
yield i;
}
}
async function start() {
var myIter = iter();
var p = await cb1(myIter);
console.log("p = ", p);
}
start();
function cb1(myIter) {
console.log("Started : " + new Date());
return new Promise((resolve, reject) => {
iteration();
function iteration() {
setTimeout(function() {
if (myIter.next().done) { // `=== done` is pointless here
console.log("End : " + new Date());
resolve('done');
} else {
console.log("---in else---");
iteration();
}
}, 3000);
}
});
}
Need 1 more function then it works:
function* iter() {
for (var i = 0; i < 10; i++) yield i
}
async function start() {
var myIter = iter();
var p = await cb1(myIter);
console.log('after await');
console.log("here is p:" + p.done + "," + p.value);
}
start();
function repeat(myIter,resolver,previous){
var temp;
if((temp = myIter.next()).done === true) {
console.log("End : " + new Date());
resolver(previous);
}else {
console.log("---in else---");
setTimeout(function(){repeat(myIter,resolver,temp)},3000);
}
}
function cb1(myIter) {
console.log("Started : " + new Date());
var obj;
return new Promise((resolve, reject) => {
repeat(myIter,resolve);
});
}
Also fixed the p, await gets it out, and you needed to grab the previous value
I have an async function that has a loop that I need to be able to pause or unpause it. This is what I have so far.
I use a flag to pause the flow:
let flag = true;
function flag_func() {
flag = !flag;
}
$(document).ready(function () {
function sleep(ms) {
while (!flag) {
//...waiting.. but infinite loop
}
return new Promise(resolve => setTimeout(resolve, ms));
}
async function show_simulation(data) {
document.getElementById("solve-button").outerHTML = "<button type=\"button\" id='pause-button' onclick='flag_func()' class=\"btn btn-primary btn-lg\">Pause</button>";
//simulation
if (data.length === 0) {
console.log('stuff')
} else {
let i;
for (i = 0; i < data.length; i++) {
await sleep(40);
// do stuff
}
}
}
});
The problem is that is being paused, but due the while block the flow, I can't unpause the for loop.
Any idea about how I can solve this?
It might be a nice use case for async iterables. It involves a bit of boilerplate to create your async list, but then the code is much nicer. Basically you would have:
import AsyncList from './async-list.js'
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
async function f(data) {
const list = new AsyncList(data);
document.getElementById("btn-toggle").addEventListener("click", function () {
if (list.paused) {
this.textContent = "Pause";
list.resume();
} else {
this.textContent = "Resume";
list.pause()
}
})
for await (let item of list) {
console.log(item)
await sleep(1000);
}
console.log("end of loop")
}
f([10, "hello", 1029, 90, 80, 209, 44])
A possible implementation of AsyncList could be:
export default class AsyncList {
constructor(array) {
// shallow copy
this._array = array.slice();
this._index = 0;
this._length = this._array.length;
this.paused = false;
this._resume = () => {}; // noop, in case `resume` is called before `pause`
}
[Symbol.asyncIterator]() {
return this;
}
pause() {
this.paused = true;
}
resume() {
this.paused = false;
this._resume();
}
next() {
if (this._index < this._length) {
const value = this._array[this._index++];
if (this.paused) {
return new Promise(r => this._resume = r.bind(null, { value }))
}
return Promise.resolve({ value })
} else {
return Promise.resolve({ done: true });
}
}
}
Just to give to you the idea, you could also encapsulate the private properties, and check more scenarios (here I assume data is an array, for example, not just an iterable).
I'd replace:
let i;
for (i = 0; i < data.length; i++) {
await sleep(40);
// do stuff
}
...with...
let i = 0;
const doStuff = () => {
// do stuff
if (++i < data.length) {
setTimeout(doStuff, 40);
}
};
setTimeout(doStuff, 40);