btnRoll.addEventListener ('click', function() {
if (isPlaying) {
diceElement.classList.remove ('hidden');
function changeDiceImage () {
// Display number on the dice
const diceNumber = Math.trunc (Math.random () * 6) + 1;
diceElement.src = `dice${diceNumber}.png`;
i++;
let timer = setTimeout (changeDiceImage, 200);
if (i === 5) {
clearTimeout (timer);
number = diceNumber;
}
timer;
}
let i = 0;
let number = 0;
changeDiceImage ();
if (number !== 1) {
currentScore += number;
document.getElementById (`current--${activePlayer}`).textContent = currentScore;
} else {
switchActivePlayer ();
}
}
})
I try to loop function (that randomly change images with dice) and also last dice number go to number then to score.
In result i receive NaN value instead of number.
How to fix the problem?
First, a couple tools...
A function that returns a promise which resolves after ms...
async function after(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
A function that returns a randomly shuffled array of [1..6], due to Mike Bostock...
function shuffleDie() {
let array = [1,2,3,4,5,6]
let m = 6, t, i;
while (m) {
i = Math.floor(Math.random() * m--);
t = array[m];
array[m] = array[i];
array[i] = t;
}
return array;
}
The OP's aim: present a random sequence of die images and return (a promise that resolves to) the last number in the random sequence...
async function changeDiceImage(diceElement) {
const numbers = shuffleDie();
for (let number of numbers) {
diceElement.src = `dice${number}.png`;
await after(ms);
}
return numbers[5]; // resolve to the last number
}
Use these to clean up the eventListener significantly...
btnRoll.addEventListener ('click', async () => {
if (isPlaying) {
diceElement.classList.remove ('hidden');
let number = await changeDiceImage(diceElement);
if (number !== 1) {
currentScore += number;
document.getElementById (`current--${activePlayer}`).textContent = currentScore;
} else {
switchActivePlayer ();
}
}
})
Related
I'm trying to print one letter after another but this code just waits 100ms and then prints the value with no pauses. Any idea why this happens and how to fix it?
Code:
for (let i = 0; i < welcText.length; i++) {
setTimeout(()=>{
welcome.innerText += welcText[i];
},100);
}
Note that setTimeout does not actually delay the running of the code that comes after; it just schedules for something to happen in the future. So all the letters are added at the same time, after 100 milliseconds.
The most straightforward way to fix it is to make each subsequent timeout wait a bit longer, by multiplying the delay with the index of the letter (i * 100):
const welcome = document.getElementById('welcome');
const welcText = 'Welcome!';
for (let i = 0; i < welcText.length; i++) {
setTimeout(() => {
welcome.innerText += welcText[i];
}, i * 100);
}
<div id="welcome"></div>
The following function divtxt() returns a Promise. You can use it to send a txt to a div with a given id and let each letter appear in ms intervals. AND: you can build a chain of any number of follow-up actions with it:
function divtxt(id, txt, ms, wait = 0) {
const div = document.getElementById(id);
return new Promise((res, rej) => {
setTimeout(() => { // optional initial timeout, when wait>0
div.textContent = "";
const a = txt.split(""),
iv = setInterval(() => {
if (a.length)
div.textContent += a.shift();
else {
clearInterval(iv);
res(txt);
}
}, ms);
}, wait);
});
}
divtxt("welcome", "Hello world, this is my way of doing it! 🤩", 100)
.then(prevText => (console.log(prevText + ' is done.'),
divtxt("descr", "You can also chain this function with any number of consecutive actions."
+" Now: wait for 2 seconds ...", 100)))
.then(() => divtxt("welcome", "This promised-based approach REALLY lets you do it!! 👍🏻", 50, 2000))
.then(() => (console.log("ready?"),"Yes! I am REALLY DONE now! 😁"))
.then(console.log)
#welcome {
font-weight: 900
}
<div id="welcome"></div>
<div id="descr"></div>
You can do it using setInterval instead, and using an iterator to call the next char of the string every time. This is a bit more complicated, but has the advantage of not having multiple schedulers running if the string is too long. See the working example below.
const welcome = document.getElementById('welcome');
const welcomeText = 'Welcome!';
// returns a function that returns the next char in the string everytime its called
function createIterator(string) {
let currentIndex = 0;
return function() {
return string[currentIndex++];
}
}
// initializes the iterator with the text
let welcomeIterator = createIterator(welcomeText);
let welcomeInterval = setInterval(function() {
let nextChar = welcomeIterator();
// if we finish the string we clear the interval
if (!nextChar) {
return clearInterval(welcomeInterval);
}
// if the char exists, we append it to the div
welcome.innerText += nextChar;
}, 100);
<div id="welcome"></div>
you can do it like this also
const welcText = 'Welcome!';
let i = 0;
const interval = setInterval(() => {
if (i >= welcText.length) {
clearInterval(interval);
} else {
document.getElementById('helloword').innerHTML += welcText[i];
i++;
}
}, 100);
<h1 id="helloword"></h1>
I have a small Node.js program that calculates two Fibonacci numbers concurrently. When both numbers have been calculated the program prints Finished. I am doing this by implementing on('exit', ...) where my callback decreases a counter and prints Finished if it reaches 0. My question is: Is there something more elegant, something like waitAll()?
Here is the program:
const { Worker, isMainThread, workerData } = require('worker_threads');
function fibonacci(n) {
if (n <= 1) {
return n;
} else {
return fibonacci(n - 1) +
fibonacci(n - 2);
}
}
let counter = 2;
if (isMainThread) {
let func = (code) => {
if (--counter === 0) {
console.log("Finished");
}
};
let w1 = new Worker(__filename, {workerData: 40});
w1.on('exit', func);
console.log("1");
let w2 = new Worker(__filename, {workerData: 45});
w2.on('exit', func);
console.log("2");
} else {
console.log(`Calculate fib(${workerData})`);
console.log(`fib(${workerData}) = ${fibonacci(workerData)}`);
}
If you promisify the event handler, you can use Promise.all:
const done = el => new Promise(res => el.on("exit", res));
Promise.all([
done(w1),
done(w2)
]).then(/*...*/)
I have array with 10 items, I calls one random item using a random number from 1 to 10, what to use to make a random number from 1 to 10, which will not happen again, and when all 10 is used, the program stops randomly? code
const num = () => Math.floor(Math.random() * 10);
const postWord = () => {
randomWord = word.innerText = words[num()].PL;
}
postWord();
submit.addEventListener("click", function() {
postWord();
});
Have you ever considered to move the array items?
var range = 10; // global variable
const num = () => Math.floor(Math.random() * range);
const postWord = () => {
randomWord = word.innerText = words[num()].PL;
for (var i=num(); i < range; i++) {
var temp = words[i];
words[i] = words[i+1];
words[i+1] = temp;
}
range--;
}
postWord();
submit.addEventListener("click", function() {
if (range) {
postWord();
}
});
I am not that familiar with JS but I guess my code can at least demonstrate my point.
I was trying to implement a function, which is supposed to post measurement A every 5 sec for 10 times, and then post measurement B every 5 sec for a random amounts of time. And I want repeat this function forever as I was trying to implement a fake agent.
So I had the code:
let intervalId = null, repeat = 0;
while (true) {
intervalId = setInterval(() => {
if (repeat < 5) {
// post measurement A
repeat += 1;
}
else {
clearInterval(intervalId)
}
}, 1000);
repeat = 0;
intervalId = setInterval(() => {
if (repeat < Math.floor(Math.random() * 11)) {
// post measurement B
repeat += 1;
}
else {
clearInterval(intervalId)
}
}, 1000);
}
The two setInterval() function didn't happen consecutively as I expected, instead they happened at the same time. And the while (true) loop seems not behave as expected either. I'm just wondering is there any way to get around with this problem? Thanks.
You can create two function, one is doA() and one is doB().
Start with doA(), count the number of time //do A is called, when it reached 10, clearInterval and call doB().
In doB(), set the min and max time it should be called, then when it reached randTime clearInterval and doA()
function doA() {
let count = 0;
const a = setInterval(() => {
//do A
console.log('do A');
count += 1;
if (count === 10) {
clearInterval(a);
doB();
}
}, 5000/10);
}
function doB() {
// set your min and max for B
const minTime = 1;
const maxTime = 10;
const randTime = Math.floor(Math.random() * (maxTime - minTime + 1)) + minTime;
let count = 0;
const b = setInterval(() => {
// do B
console.log(randTime);
console.log('do B');
count += 1;
if (count === randTime) {
clearInterval(b);
doA();
}
}, 5000 / randTime);
}
doA();
Working on top of your code, first thing first, remove infinite while loop. It will run endlessly in synchronous fashion while setInterval is asynchronous. repeat value will be far ahead before you do repeat += 1.
Second, break them down in function so they have their own closure for intervalId and repeat value.
function intervalA () {
let intervalId = null
let repeat = 0
intervalId = setInterval(() => {
if (repeat < 5) {
console.log(new Date(), 'A')
// post measurement A
repeat += 1; // increment repeat in callback.
}
else {
clearInterval(intervalId); // done with interval, clear the interval
intervalB(); // and start interval B
}
}, 1000)
}
function intervalB () {
let repeat = 0
let randomEnd = Math.floor(Math.random() * 11) // calculate when it should end.
let intervalId = setInterval(() => {
if (repeat < randomEnd) {
console.log(new Date(), 'B will finish in', randomEnd, 'times')
repeat += 1
}
else {
clearInterval(intervalId) // clear the interval once done
}
}, 1000)
}
intervalA(); //start with interval A
Currently, the intervals are being set at once, synchronously, at the start of your script and during every while thereafter. It would probably be clearer if you only a single interval, with a variable that indicated which measurement to run, and change that variable every random-nth iteration:
const getRandomRepeats = () => Math.ceil(Math.random() * 11)
let repeatsRemaining = getRandomRepeats();;
let measureA = true;
setInterval(() => {
repeatsRemaining--;
if (repeatsRemaining === 0) {
repeatsRemaining = getRandomRepeats();
measureA = !measureA;
}
console.log('repeats remaining: ' + repeatsRemaining);
if (measureA) {
console.log('posting a');
} else {
console.log('posting b');
}
}, 1000);
I'm a beginner with JavaScript so please be patient =)
I am trying to write a function that counts the number of times it is called. What I have so far is a function with a counter that is incremented explicitly:
var increment = function () {
var i = 0;
this.inc = function () {i += 1;};
this.get = function () {return i;};
};
var ob = new increment();
ob.inc();
ob.inc();
alert(ob.get());
But I'm wondering how to call only ob();, so the function could increment calls made to itself automatically. Is this possible and if so, how?
var increment = function() {
var i = 0;
return function() { return i += 1; };
};
var ob = increment();
ob = function f(){
++f.count || (f.count = 1); // initialize or increment a counter in the function object
return f.count;
}
Wrap a counter to any function:
/**
* Wrap a counter to a function
* Count how many times a function is called
* #param {Function} fn Function to count
* #param {Number} count Counter, default to 1
*/
function addCounterToFn(fn, count = 1) {
return function () {
fn.apply(null, arguments);
return count++;
}
}
See https://jsfiddle.net/n50eszwm/
A one liner option:
const counter = ((count = 0) => () => count++)()
Usage example:
> counter()
0
> counter()
1
> counter()
2
> counter()
3
> counter()
4
> counter()
5
> counter()
6
There are also the new Generator functions, which offer a simple way to write a counter:
function* makeRangeIterator(start = 0, end = 100, step = 1) {
let iterationCount = 0;
for (let i = start; i < end; i += step) {
iterationCount++;
yield i;
}
return iterationCount;
}
const counter = makeRangeIterator();
const nextVal = () => counter.next().value;
console.log("nextVal: ", nextVal()); // 0
console.log("nextVal: ", nextVal()); // 1
console.log("nextVal: ", nextVal()); // 2
console.log("nextVal: ", nextVal()); // 3