Edit: can you spot the bugs in this poker game? it's driving me crazy. the promise isn't working. plus poker is played with max 9 players. I managed to have 10 players although I specified the number of players. I don't know what's going on. Can somebody help me?
function playerActs(i, s) {
return new Promise(resolve => {
console.log(`player ${i+1} took ${(s/1000).toFixed(2)} secs to decide`);
resolve('finished');
});
}
async function round() {
let hasEveryoneSpoken = false;
let hasEveryoneChecked = false;
let isEveryoneAllIn = false;
let stopBettingRound = false;
let nbPlayers = 9;
let i = 0;
while (!stopBettingRound) {
let delay = (Math.random() * 1000 * 5);
// wait until the player take a decision
let res = await setTimeout(playerActs, delay, i, delay);
console.log(res);
// dummy condition to exit loop
if (i == nbPlayers) hasEveryoneSpoken = true;
stopBettingRound =
hasEveryoneSpoken || hasEveryoneChecked || isEveryoneAllIn;
i++;
}
}
round();
You can't use setInterval like that because as with Promises, it doesn't block code execution, so the code falls straight through to your call to resolve.
I would suggest that you refactor the entire count-down logic into its own function that also wraps a Promise that you can await for.
That said, I also don't see any logic yet to allow you to cancel the countdown if the user proceeds with their next move while you're waiting.
Related
Problem
I am building algorithms simulator tool to simulate how algorithms work.
In BFS Algorithm I wanted to slow down the result display
So I used setTimeout function after each step to wait about 10ms before hitting the next step.
I used promise to be able to use async to easily wait the whole period and force the algorithm to stop till the wait function finishes
function implementation
function wait(time) {
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, time);
})
}
BFS
while (queue.length) {
let currentId = queue[index++];
let tile = board.tileOf(currentId);
tile.visit();
if (currentId == targetId) {
return;
}
await wait(10);
for (let direction of DIRECTIONS) {
let childId = moveId(currentId, direction);
if (childId == undefined)
continue;
childId = parseInt(childId);
let child = board.tileOf(childId);
if (child.available()) {
child.visit(true);
queue.push(childId);
pervNode[childId] = currentId;
}
}
}
the problem is when I run the code it works fine but sometimes it takes very long time to display the solution more and more than just 10ms.
I am wondering why it's not accurate?
is this because of the approach that I am using?
and if there is a better solution what could it be?
Try the tool from here
i don't know the actual output but i think that could work
while (queue.length) {
let currentId = queue[index++];
let tile = board.tileOf(currentId);
tile.visit();
if (currentId == targetId) {
return;
}
let timer;
const loop = () => {
for (let direction of DIRECTIONS) {
let childId = moveId(currentId, direction);
if (childId == undefined) continue;
childId = parseInt(childId);
let child = board.tileOf(childId);
if (child.available()) {
child.visit(true);
queue.push(childId);
pervNode[childId] = currentId;
}
}
};
const listener = () => {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
loop();
}, 100);
};
listener();
}
As JavaScript is a single-thread language, when you place a function call inside setTimeout(), this function gets in a queue and waits until other functions in a call stack (that were called before it) are finished and the call stack become empty. Time you specify as a second parameter to setTimeout() starts counting from the moment of placing the function in the queue, but not always the queue is empty
I am making a sorting algorithm visualizer using JS, HTML and CSS and for showing the bubble sort I have written the following code
for (let i = 0; i < elements; i++) {
for (let j = 0; j < elements; j++) {
let a = temp[i].style.height;
let b = temp[j].style.height;
//this is to show the current elements begin compared
temp[i].style.backgroundColor = 'green';
temp[j].style.backgroundColor = 'blue';
if (parseFloat(a) < parseFloat(b)) {
//if the elements need to be swapped then the following code will change its height
let t = a;
temp[i].style.height = b;
temp[j].style.height = t;
}
//this is to slow down the process
sleep(500);
//this is to change back the div's background to normal
temp[i].style.backgroundColor = 'white';
temp[j].style.backgroundColor = 'white';
}
}
}
function sleep(num) {
var now = new Date();
var stop = now.getTime() + num;
while (true) {
now = new Date();
if (now.getTime() > stop) return;
}
}
but here the problem is that I am not able to see any intermediate results like seeing two divs being colored and changing height.
I am only able to see the whole thing sorted when the sorting is done
So what is the problem here ?
How to solve this ?
While your code is running, the UI is not updating (there won't be any rendering, otherwise webpages would flicker if they would have dynamic content being updated by JavaScript!), so your busy waiting will do nothing other than wasting CPU cycles.
Make the whole thing asynchronous and wait using a timeout: const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)) will give you an async function that sleeps for the given amount of time, and then you can make your code an async function (so you can use await) and use await sleep(500) instead of sleep(500).
Since the code is now no longer synchronous, it won't block the event loop, and will allow the UI to update while you are waiting.
Here is a working example of asynchronous waiting:
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
const counter = document.getElementById('counter')
const countButton = document.getElementById('count-button')
async function count () {
countButton.disabled = true
counter.innerText = 'One...'
await sleep(1000)
counter.innerText = 'Two...'
await sleep(1000)
counter.innerText = 'Three...'
await sleep(1000)
counter.innerText = 'Done!'
countButton.disabled = false
}
countButton.addEventListener('click', () => {
// It's important to catch any asynchronous error here so it can be handled
// regardless of where it happens in the process - otherwise it will become
// an unhandled promise rejection.
count().catch(e => console.error('An error occured during counting!', e))
})
<h1 id="counter">...</h1>
<button id="count-button">Count!</button>
I have an API which is limited regarding how many requests per minute (50/minute) I can send to any endpoint provided by that API.
In the following code-section, I filter the objects orders with an URL as property, every object with an URL that provides data should be stored in successfullResponses in my app.component.ts.
Promise.all(
orders.map(order => this.api.getURL(order.resource_url).catch(() => null))
).then(responses => {
const successfulResponses = responses.filter(response => response != null)
for(let data of successfulResponses) {
// some other requests should be sent with data here
}
});
There are more than 50 orders to check, but I just can check maximum 50 orders at once, so I try to handle it in my service. I set the first date when the first request is sent. After that I compare the dates of the new request with the first one. If the difference is over 60, I set the current date to the new one and set maxReq again to 50. If it is under 60, I check if there are requests left, if yes I send the request and if not I just wait one minute :
sleep(ms){
return new Promise(resolve => setTimeout(resolve, ms));
}
async getURL(){
if(!this.date){
let date = new Date();
this.date = date;
}
if((new Date().getSeconds() - this.date.getSeconds() > 60 )){
this.maxReq = 50;
this.date = new Date();
return this.http.get(url, this.httpOptions).toPromise();
} else {
if(this.maxReq > 0){
this.maxReq -= 1;
return this.http.get(url, this.httpOptions).toPromise();
} else{
console.log("wait");
await this.sleep(60*1000);
this.maxReq = 50;
this.date = new Date();
return this.http.get(url, this.httpOptions).toPromise();
}
}
}
However the code in app.component.tsis not waiting for the function getURL() and executes further code with requests which leads to the problem that I send ´too many requests too quickly´.
What can I do about that?
I had a similar problem while trying to use promises with multiple async functions. It's an easy thing to forget, but in order to make them all wait, you have to use await on the root line that calls the function in question.
I'm not entirely certain, but my presumption is that your await this.sleep(60*1000); line is indeed waiting for a timeout to occur, but whilst it is doing this, the code that called getURL() is executing the rest of its lines, because it did not have an await (or equivalent, like .then) before getURL().
The way I discovered this in my case was by using a good debugging tool (I used Chrome DevTools's own debugging features). I advise you do the same, adding breakpoints everywhere, and see where your code is going with each line.
Here is a short, rough example to show what I mean:
// This code increments a number from 1 to 2 to 3 and returns it each time after a delay of 1 second.
async function loop() {
for (i = 1; i <= 3; i++) {
console.log('Input start');
/* The following waits for result of aSync before continuing.
Without 'await', it would execute the last line
of this function whilst aSync's own 'await'
waited for its result.
--- This is where I think your code goes wrong. --- */
await aSync(i);
console.log('Input end');
}
}
async function aSync(num) {
console.log('Return start');
/* The following waits for the 1-second delay before continuing.
Without 'await', it would return a pending promise immediately
each time. */
let result = await new Promise(
// I'm not using arrow functions to show what it's doing more clearly.
function(rs, rj) {
setTimeout(
function() {
/* For those who didn't know, the following passes the number
into the 'resolved' ('rs') parameter of the promise's executor
function. Without doing this, the promise would never be fulfilled. */
rs(num);
}, 1000
)
}
);
console.log(result);
console.log('Return end');
}
loop();
I'm receiving data in browser through websockets (paho-mqtt) but problem is that the receiving callback gets fired only when another task ends (big for loop) and it gets fired with all the stacked data, I'm not losing data just getting delayed. Shouldn't the callback get fired even if there is a loop running? What is happening here?. Otherwise, how can I achieve this, keep receiving while inside a loop?
What I'm trying to say is equivalent to the following:
If I do this in chrome
setTimeout(() => {
console.log('hello!');
}, 10);
for (var i = 0; i < 50000; i++) {
console.log('for array');
}
I get
50000 VM15292:5 for array
VM15292:2 hello!
Shouldn't I get something like this?
1000 VM15292:5 for array
VM15292:2 hello!
49000 VM15292:5 for array
When you run JavaScript code in the browser (unless using Web Workers or other special technologies), it is executed on a single thread. That might not sound too important, but it is.
Your code consists of a for-loop (synchronous) and a call to setTimeout (asychronous). Since only one piece of JavaScript can be running at once, your for-loop will never be interrupted by setTimeout.
In fact, if your for-loop contained extremely intensive operations that required more than 10 ms to complete, your setTimeout callback might actually be delayed past that mark, because the browser always wait for the currently executing code to finish before continuing to run the event loop.
setTimeout(() => {
console.log('hello!');
}, 10);
for (var i = 0; i < /* 50000 */ 5; i++) {
console.log('for array');
}
The others have diagnosed the problem well, the single threaded nature of the browser. I will offer a possible solution: generators.
Here's a codepen which demonstrates the problem:
http://codepen.io/anon/pen/zZwXem?editors=1111
window.CP.PenTimer.MAX_TIME_IN_LOOP_WO_EXIT = 60000;
function log(message) {
const output = document.getElementById('output');
output.value = output.value + '\n' + message;
}
function asyncTask() {
log('Simulated websocket message')
}
function doWork() {
const timer = setInterval(1000, asyncTask);
let total = 0;
for (let i = 1; i < 100000000; i++) {
const foo = Math.log(i) * Math.sin(i);
total += foo;
}
log('The total is: '+ total);
clearInterval(timer);
}
When doWork() is called by clicking the 'Do Work' button, the asyncTask never runs, and the UI locks up. Horrible UX.
The following example uses a generator to run the long running task.
http://codepen.io/anon/pen/jBmoPZ?editors=1111
//Basically disable codepen infinite loop detection, which is faulty for generators
window.CP.PenTimer.MAX_TIME_IN_LOOP_WO_EXIT = 120000;
let workTimer;
function log(message) {
const output = document.getElementById('output');
output.value = output.value + '\n' + message;
}
function asyncTask() {
log('Simulated websocket message')
}
let workGenerator = null;
function runWork() {
if (workGenerator === null) {
workGenerator = doWork();
}
const work = workGenerator.next();
if (work.done) {
log('The total is: '+ work.value);
workerGenerator = null;
} else {
workTimer = setTimeout(runWork,0);
}
}
function* doWork() {
const timer = setInterval(asyncTask,1000);
let total = 0;
for (let i = 1; i < 100000000; i++) {
if (i % 100000 === 0) {
yield;
}
if (i % 1000000 == 0) {
log((i / 100000000 * 100).toFixed(1) + '% complete');
}
const foo = Math.log(i) * Math.sin(i);
total += foo;
}
clearInterval(timer);
return total;
}
Here we do work in a generator, and create a generator runner to call from the 'Do Work' button in the UI. This runs on the latest version of Chrome, I can't speak for other browsers. Typically you'd use something like babel to compile the generators down to ES5 syntax for a production build.
The generator yields every 10000 rows of calculation, and emits a status update every 100000 rows. The generator runner 'runWork' creates an instance of the generator and repeatedly calls next(). The generator then runs until it hits the next 'yield' or return statement. After the generator yields, the generator runner then gives up the UI thread by calling setTimeout with 0 milliseconds and using itself as the handler function. This typically means it will get called once every animation frame (ideally). This goes until the generator returns the done flag, at which point the generator runner can get the returned value and clean up.
Here the HTML for the example in case you need to recreate the codepen:
<input type='button' value='Do Work' onclick=doWork() />
<textarea id='output' style='width:200px;height:200px'></textarea>
Javascript engines tends to be single threaded.
So if you are in a long running tight loop that doesn't yield (e.g. to do some io) then the callback will never get a chance to run until the loop finishes
I've got a method that returns a promise and internally that method makes a call to an API which can only have 20 requests every minute. The problem is that I have a large array of objects (around 300) and I would like to make a call to the API for each one of them.
At the moment I have the following code:
const bigArray = [.....];
Promise.all(bigArray.map(apiFetch)).then((data) => {
...
});
But it doesnt handle the timing constraint. I was hoping I could use something like _.chunk and _.debounce from lodash but I can't wrap my mind around it. Could anyone help me out ?
If you can use the Bluebird promise library, it has a concurrency feature built in that lets you manage a group of async operations to at most N in flight at a time.
var Promise = require('bluebird');
const bigArray = [....];
Promise.map(bigArray, apiFetch, {concurrency: 20}).then(function(data) {
// all done here
});
The nice thing about this interface is that it will keep 20 requests in flight. It will start up 20, then each time one finishes, it will start another. So, this is a potentially more efficient than sending 20, waiting for all to finish, sending 20 more, etc...
This also provides the results in the exact same order as bigArray so you can identify which result goes with which request.
You could, of course, code this yourself with generic promises using a counter, but since it is already built in the the Bluebird library, I thought I'd recommend that way.
The Async library also has a similar concurrency control though it is obviously not promise based.
Here's a hand-coded version using only ES6 promises that maintains result order and keeps 20 requests in flight at all time (until there aren't 20 left) for maximum throughput:
function pMap(array, fn, limit) {
return new Promise(function(resolve, reject) {
var index = 0, cnt = 0, stop = false, results = new Array(array.length);
function run() {
while (!stop && index < array.length && cnt < limit) {
(function(i) {
++cnt;
++index;
fn(array[i]).then(function(data) {
results[i] = data;
--cnt;
// see if we are done or should run more requests
if (cnt === 0 && index === array.length) {
resolve(results);
} else {
run();
}
}, function(err) {
// set stop flag so no more requests will be sent
stop = true;
--cnt;
reject(err);
});
})(index);
}
}
run();
});
}
pMap(bigArray, apiFetch, 20).then(function(data) {
// all done here
}, function(err) {
// error here
});
Working demo here: http://jsfiddle.net/jfriend00/v98735uu/
You could send 1 block of 20 requests every minute or space them out 1 request every 3 seconds (latter probably preferred by the API owners).
function rateLimitedRequests(array, chunkSize) {
var delay = 3000 * chunkSize;
var remaining = array.length;
var promises = [];
var addPromises = function(newPromises) {
Array.prototype.push.apply(promises, newPromises);
if (remaining -= newPromises.length == 0) {
Promise.all(promises).then((data) => {
... // do your thing
});
}
};
(function request() {
addPromises(array.splice(0, chunkSize).map(apiFetch));
if (array.length) {
setTimeout(request, delay);
}
})();
}
To call 1 every 3 seconds:
rateLimitedRequests(bigArray, 1);
Or 20 every minute:
rateLimitedRequests(bigArray, 20);
If you prefer to use _.chunk and _.debounce1 _.throttle:
function rateLimitedRequests(array, chunkSize) {
var delay = 3000 * chunkSize;
var remaining = array.length;
var promises = [];
var addPromises = function(newPromises) {
Array.prototype.push.apply(promises, newPromises);
if (remaining -= newPromises.length == 0) {
Promise.all(promises).then((data) => {
... // do your thing
});
}
};
var chunks = _.chunk(array, chunkSize);
var throttledFn = _.throttle(function() {
addPromises(chunks.pop().map(apiFetch));
}, delay, {leading: true});
for (var i = 0; i < chunks.length; i++) {
throttledFn();
}
}
1You probably want _.throttle since it executes each function call after a delay whereas _.debounce groups multiple calls into one call. See this article linked from the docs
Debounce: Think of it as "grouping multiple events in one". Imagine that you go home, enter in the elevator, doors are closing... and suddenly your neighbor appears in the hall and tries to jump on the elevator. Be polite! and open the doors for him: you are debouncing the elevator departure. Consider that the same situation can happen again with a third person, and so on... probably delaying the departure several minutes.
Throttle: Think of it as a valve, it regulates the flow of the executions. We can determine the maximum number of times a function can be called in certain time. So in the elevator analogy.. you are polite enough to let people in for 10 secs, but once that delay passes, you must go!