How can I setTimeout my recursive function - javascript

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')
})();

Related

How can I make the function in setTimeout execute completely?

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");

JavaScript Throttle always returning function

I'm trying to understand JavaScript Throttling. I implemented a very basic throttle for the knowledge that I have so far.
const display = (msg) => {
return msg;
}
const throttleDisplay = (func, limit) => {
let flag = true;
return function() {
if(flag) {
func.apply(this, arguments);
flag = false;
setTimeout(() => flag = true, limit);
}
}
}
for(let i=1; i<=5; i++) {
setTimeout(() => {
const result = throttleDisplay(display("Hi"), 6000);
console.log(result)
}, i*1000);
}
My console.log is returning [Function] instead of the message Hi. Also, it is returning [Function] 5 times. Shouldn't it ignore the next call until the limit, 6000ms, has passed?
Thanks a lot!
Throttling works differently.
First, you should name your throttle function just throttle, as it is not specifically linked with display. Then, you should call this throttle to get a function that is specific to display. And only then you would use that secondary function in the actual use case you have for it, i.e. with the timer.
Here is your code corrected:
const throttle = (func, limit) => {
let flag = true;
return function() {
if(flag) {
func.apply(this, arguments);
flag = false;
setTimeout(() => flag = true, limit);
}
}
};
const throttleDisplay = throttle(() => console.log("Hi"), 6000);
for(let i=1; i<=10; i++) {
setTimeout(throttleDisplay, i*1000);
}

Promise await blocking loop from finishing

I need a loop to play 10 sounds in sequence. My first attempt had the sounds overlapping, so I was told that I need to use Promise/await. The code below plays sound 0 then never continues the loop.
(The library I'm using (jscw) is for morse code. You pass it a string, it plays the morse equivalent. Its "onFinished" calls a user-defined function.)
async function playAll() {
for (let i = 0; i < 10; i++) {
playMorse(words[i]);
await playstate();
}
}
function playstate() {
playdone = true;
//console.log(playdone);
return new Promise((resolve) => {
window.addEventListener('playdone', resolve)
})
}
function playMorse(z) {
var m = new jscw();
playdone = false;
m.onFinished = function() {
playstate();
}
m.play(z);
}
It seems nothing is supposed to fire the playdone event you are listening for.
So a simple solution is to fire it in the onFinished callback.
const words = ["hello", "world"];
async function playAll() {
for (let i = 0; i < 2; i++) {
console.log("###READING", words[i]);
playMorse(words[i]);
await playstate();
}
}
function playstate() {
playdone = true;
//console.log(playdone);
return new Promise((resolve) => {
window.addEventListener('playdone', resolve, { once: true })
})
}
function playMorse(z) {
var m = new jscw();
playdone = false;
m.onFinished = function() {
dispatchEvent( new Event("playdone") );
}
m.play(z);
}
btn.onclick = playAll;
<script src="https://fkurz.net/ham/jscwlib/src/jscwlib.js"></script>
<button id="btn">play all</button>
But you don't need an event here, simply make playMorse return a Promise that will resolve in the onFinished callback:
const words = ["hello", "world"];
async function playAll() {
for (let i = 0; i < 2; i++) {
console.log("###READING", words[i]);
await playMorse(words[i]);
}
}
function playMorse(z) {
return new Promise( (resolve) => {
const m = new jscw();
m.onFinished = resolve;
m.play(z);
});
}
btn.onclick = playAll;
<script src="https://fkurz.net/ham/jscwlib/src/jscwlib.js"></script>
<button id="btn">play all</button>

Pause and unpause loop in javascript within an async function

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);

jQuery appended element using recursion is not displayed one by one

I am trying to append paragraph's to a container using recursive function.
The code looks like this -
loadSections (sections, buttons) {
let sectionLength = sections.length;
let self = this;
this.sleep(sections[sectionCounter].DelayInMs);
let context = {
message: sections[sectionCounter].Text,
};
this.fillSections(context, function () {
sectionCounter ++;
if (sectionCounter < sectionLength) {
self.loadSections(sections, buttons);
}
});
}
fillSections (context, callback) {
let messageList = $(document.createElement('p'));
messageList.html(context.message);
$('.chat-flow-container').append(messageList);
callback();
}
sleep (milliSeconds) {
let start = new Date().getTime();
let expire = start + milliSeconds;
while (new Date().getTime() < expire) { }
return;
}
The code just works. But the issue is all appended p elements into .chat-flow-container are not shown with the delay with I mentioned in sleep method, instead they all are shown together once the recursion ends.
The busy loop in the sleep method is preventing your browser from updating the screen. It blocks the browser. So this is not a good way to proceed.
Instead make your code asynchronous. If you have support for async/await then you could do it like this:
async loadSections(sections, buttons) {
for (let section of sections) {
await this.sleep(section.DelayInMs);
let context = {
message: section.Text,
};
this.fillSections(context);
}
}
fillSections (context) {
let messageList = $(document.createElement('p'));
messageList.html(context.message);
$('.chat-flow-container').append(messageList);
}
sleep (milliSeconds) {
return new Promise(resolve => setTimeout(resolve, milliSeconds));
}
Now loadSections will return immediately, but the code in it will still resume later when the setTimeout expires.
const sections = [
{ DelayInMs: 300, Text: 'A' },
{ DelayInMs: 1000, Text: 'B' },
{ DelayInMs: 1000, Text: 'C' },
];
class X {
async loadSections(sections, buttons) {
for (let section of sections) {
await this.sleep(section.DelayInMs);
let context = {
message: section.Text,
};
this.fillSections(context);
}
}
fillSections (context) {
let messageList = $(document.createElement('p'));
messageList.html(context.message);
$('.chat-flow-container').append(messageList);
}
sleep (milliSeconds) {
return new Promise(resolve => setTimeout(resolve, milliSeconds));
}
}
new X().loadSections(sections);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="chat-flow-container"></div>

Categories