I'm trying to understand how Promises are working and I can't get my piece of code to work.
class Lights {
constructor(delay) {
this.blue = 0;
this.green = 0;
this.red = 0;
this.delay = delay;
}
fadeIn(color, i) {
var self = this;
return new Promise(function(resolve, reject) {
setTimeout(function () {
self[color] = i;
console.log(self[color]);
i+=5;
if (i <= 255) {
self.fadeIn(color, i);
}
resolve(self);
}, self.delay);
});
}
fadeOut(color, i) {
var self = this;
return new Promise(function(resolve, reject) {
setTimeout(function () {
self[color] = i;
console.log(self[color]);
i-=5;
if (i >= 0) {
self.fadeIn(color, i);
}
resolve(self);
}, self.delay);
});
}
}
var lights = new Lights(50);
lights.fadeIn("blue", 0).then(
lights.fadeOut("blue", 255)
);
Here is a jsFiddle of the code.
The idea behind the code is to set the blue color from 0 to 255 and Then from 255 to 0. How can I do this ?
You are making recursive calls so on the last call what you resolve is not the resolve in your first promise that you call then on so you could store that first resolve in one property in your class and then call it.
class Lights {
constructor(delay) {
this.blue = 0;
this.green = 0;
this.red = 0;
this.delay = delay;
this.fadeInResolve = null;
this.fadeOutResolve = null;
}
fadeIn(color, i) {
return new Promise((resolve, reject) => {
if (!this.fadeInResolve) {
this.fadeInResolve = resolve
}
setTimeout(() => {
this[color] = i;
console.log(this[color]);
i += 5;
if (i <= 255) this.fadeIn(color, i);
else this.fadeInResolve(this)
}, this.delay);
});
}
fadeOut(color, i) {
return new Promise((resolve, reject) => {
if (!this.fadeOutResolve) {
this.fadeOutResolve = resolve
}
setTimeout(() => {
this[color] = i;
console.log(this[color]);
i -= 5;
if (i >= 0) this.fadeOut(color, i);
else this.fadeOutResolve(this)
}, this.delay);
});
}
}
var lights = new Lights(50);
lights.fadeIn("blue", 0).then(() => {
console.log('Fade in done')
lights.fadeOut("blue", 255).then(() => {
console.log('Fade out done')
})
});
Promise.prototype.then() should take a callback function and the recursion is not waiting. Consider this code which can be used to do the same thing:
//promisify :)
function timer(delay) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve();
}, delay);
});
}
class _Modulator {
constructor(_resolution = 255, _delay = 5) {
/* assert resolution and delay > 0; */
this._resolution = _resolution;
this._delay = _delay;
this._counter = 0;
this._running = false;
}
start() {
console.log("timer start");
this._running = true;
this._start();
}
_start() {
return timer(this._delay).then(() => {
if (this._running === true) {
this._counter += 1;
console.log("tick");
this._onTick();
/* care should be taken to ensure this will always catch, e.g.,
* correcting init
*/
if (this._counter === this._resolution) {
this._counter = 0;
this._onCycle();
}
this._start();
}
});
}
stop() {
this._running = false;
console.log("timer stopped");
}
_onTick() {
console.log("tick handle: %s", this._counter);
}
_onCycle() {
console.log("new cycle");
}
}
class UpDownModulator extends _Modulator {
constructor(_resolution = 255, _delay = 5) {
super(_resolution, _delay);
this._dir = 1;
}
_onTick() {
console.log("tick handle: %s", this.getCounter());
}
_onCycle() {
this._toggleDirection();
console.log("new cycle: going %s", this.getDirection());
}
_toggleDirection() {
this._dir ^= 1;
}
getCounter() {
return this._dir
? this._counter
: this._resolution - this._counter;
}
getDirection() {
return this._dir ? "up" : "down";
}
}
let c = new UpDownModulator();
c.start();
You can create a ColorFader class that depends on a Modulator and observe it. This creates clean abstractions that adhere to SRP.
I hope this helps!
Related
I am trying to create a visual selection sort. finMin() will go through the array one by one displaying the new min when found. I want to use this function in a loop for selection sort. If the function is run one time, then everything is fine, but if findMin() is run in a loop, then the function has bugs.
If a function is run in a loop such as for(let i=0; i<3; i++){findMin();} does the second iteration of the loop run immediately or does it wait for findMin to return before i == 1? I believe that this loop should be sequential, but I do not know why the code does not work in a loop then.
var gBars = [];
var gSelected = 19;
var gFinished = 19;
var changed = false;
var step = 0;
function Bar(index, height){
this.index = index;
this.height = height;
this.getIndex = function(){
console.log(this.index);
};
this.getHeight = function(){
console.log(this.height);
};
this.getStats = function(){
console.log(this.index + ' ' + this.height);
}
this.setHeight = function(h){
this.height = h;
}
this.setIndex = function(i){
this.index = i;
}
}
function insertAfter(newNode, referenceNode){
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}
function setHeight(i, h){
document.getElementById(i).style.height = h + 'em';
}
function addBar(i, h){
//base case i = 0
//first bar
if(i === 0){
var currentDiv = document.getElementById("root");
d = document.createElement('div');
d.setAttribute("id", 'block'+i);
d.setAttribute("class", 'block');
gBars[i] = new Bar(i, h);
currentDiv.appendChild(d);
setHeight('block'+i,h);
}
else {
let last = i-1;
var currentDiv = document.getElementById('block'+last);
d = document.createElement('div');
d.setAttribute("id", 'block'+i);
d.setAttribute("class", 'block');
gBars[i] = new Bar(i, h);
insertAfter(d, currentDiv);
setHeight('block'+i,h);
}
}
function selSort(){
for(let i=0; i<10; i++){
findMin(gFinished);
}
}
function findMin(gFinished) {
let min = gBars[gFinished].height;
//start at 18 because bars are rotated 180deg
//go backwards so it appears to go forwards
var delay = 500;
let i = gFinished - 1;
min = setTimeout(timeout(i, min), delay);
return min;
}
function timeoutchange(){
var swapped = document.getElementById('block'+gFinished);
var selected = document.getElementById('block'+gSelected);
let temp = gBars[gFinished].height;
swapped.style.height = gBars[gSelected].height + 'em';
selected.style.height = temp + 'em';
selected.style.backgroundColor = "grey";
var selected = document.getElementById('block'+gFinished);
selected.style.backgroundColor = "green";
gFinished--;
var selected = document.getElementById('block'+gFinished);
selected.style.backgroundColor = "blue";
gSelected = gFinished;
}
function timeout(i, min) {
console.log("Next loop: " + i);
if(i==18){
var selected = document.getElementById('block19');
selected.style.backgroundColor = "blue";
}
if(min > gBars[i].height) {
min = gBars[i].height;
var selected = document.getElementById('block'+i);
selected.style.backgroundColor = "blue";
console.log('new min ' + min);
selected = document.getElementById('block'+gSelected);
selected.style.backgroundColor = "grey";
gSelected = i;
}
i--;
if (i == 0) {
console.log("End");
var swapped = document.getElementById('block'+gFinished);
swapped.style.backgroundColor = "red";
setTimeout(function(){
return timeoutchange();
},1000)
step++;
return min;
} else {
setTimeout(function(){
return timeout(i, min);
},500)
}
}
function init(){
for(let i=0; i<20; i++){
let ran = Math.floor(Math.random() * 50 + 1);
gBars[i] = new Bar(i,ran);
addBar(i,ran);
}
for(let i=0; i<20; i++){
gBars[i].getStats();
}
//works
findMin(gFinished);
//findMin does not work in loop
//why?
//selSort();
return;
}
init();
.selected{
background-color:blue;
}
.block{
border:1px solid rgba(0,0,0,.4);
width:20px;
background-color:grey;
}
#root{
display:flex;
transform:rotate(180deg);
position:absolute;
left:10%;
}
<html>
<head>
<link rel="stylesheet" href="style.css">
</head>
<body>
<button>sort</button>
<div id="root"></div>
</body>
<script src="selectionsort.js"></script>
</html>
What you want to do is use JavaScript Promises. (https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise) There you have a concept called chaining so that you can chain your functions one after the other based on execution (in this case resolved). For an example Let say you have to functions:
function a() {
setTimeout( function() {
resolve("Success!") // Yay! Everything went well!
}, 250)
}
function b() {
setTimeout( function() {
resolve("Success 2!") // Yay! Everything went well!
}, 250)
}
you can make these promises and chain them one after the other:
let a = new Promise((resolve, reject) => {
setTimeout( function() {
resolve("Success!") // Yay! Everything went well!
}, 250)
})
let b = new Promise((resolve, reject) => {
setTimeout( function() {
resolve("Success2!") // Yay! Everything went well!
}, 250)
})
let c = new Promise((resolve, reject) => {
setTimeout( function() {
resolve("Success3!") // Yay! Everything went well!
}, 250)
})
a().then(()=>{
return b();
}).then(()=>{
return c();
});
setTimeout returns a number representing the id of the timer, so when running findMin() in a loop it will return that, and immediately after execute the next iteration.
To make the loop wait for the timeout you'll have to await for a promise that is resolved after the delay
for (let i = 0; i < 3; i++) {
min = await new Promise((resolve) => {
setTimeout(() => {
resolve(timeout(i, min))
}, 500);
})
}
I am working on a game using HTML and Javascript. And I need a function to be called every 2 seconds. I tried using setInterval but the function is not being called. And the browser console does not show any errors.
function Apples() {
this.Apples = [];
this.indexCount = 0;
this.CurrentTime;
this.createApples = function() {
this.Apples[this.indexCount] = new Apple();
this.indexCount++;
}
this.updateApples = function() {
for (var i = 0; i < this.indexCount; i++) {
this.Apples[i].positionY += 1;
this.Apples[i].drawApple();
}
}
}
var apples = new Apples;
setInterval(apples.createApples, 200);
It does not work because of the context of this and your code is 200 milliseconds, not 2 seconds.
function Apple () {
return {
positionY: 0,
drawApple: function () {}
}
}
function Apples() {
this.Apples = [];
this.indexCount = 0;
this.CurrentTime;
this.createApples = function() {
this.Apples[this.indexCount] = new Apple();
this.indexCount++;
console.log("new index", this.indexCount);
}
this.updateApples = function() {
for (var i = 0; i < this.indexCount; i++) {
this.Apples[i].positionY += 1;
this.Apples[i].drawApple();
}
}
}
var apples = new Apples;
// use bind
// setInterval(apples.createApples.bind(apples), 2000);
// or use function
setInterval(() => apples.createApples(), 2000);
//setInterval(function () { apples.createApples(); }, 2000);
I am trying to implement two classes that can deal with asynchronous tasks in JavaScript:
Class Task: mimics the execution of a task with setTimeout. Once the timer expires, the task is considered completed.
Class TaskManager: has a capacity parameter to limit the numbers of tasks that can be executing in parallel.
I thought if I could just call the loop function recursively, just to keep checking if one job is done, I could proceed to the next job. But this leads immediately to a "Maximum call stack size exceeded" error.
Can someone explain how I can fix this?
class Task {
constructor(time) {
this.time = time;
this.running = 0;
}
run(limit, jobs, index) {
setTimeout(() => {
console.log('hello', index);
this.done(limit, jobs, index);
}, this.time);
}
done(limit, jobs, index) {
jobs.splice(index, 1);
console.log(jobs);
}
}
class TaskManager {
constructor(capacity) {
this.capacity = capacity;
this.jobs = [];
this.index = 0;
this.running = 0;
this.pending = [];
}
push(tk) {
this.jobs.push(tk);
this.index += 1;
const loop = () => {
if (this.jobs.length === 0) {
return;
}
if (this.jobs.length <= this.capacity) {
this.running += 1;
tk.run(this.capacity, this.jobs, this.index-1);
return;
}
loop();
}
loop();
}
}
const task = new Task(100);
const task1 = new Task(200);
const task2 = new Task(400);
const task3 = new Task(5000);
const task4 = new Task(6000);
const manager = new TaskManager(3);
manager.push(task);
manager.push(task1);
manager.push(task2);
manager.push(task3);
manager.push(task4);
You should not implement the busy loop, as that will block the event loop and so no user UI events or setTimeout events will be processed.
Instead respond to asynchronous events.
If you let the setTimeout callback resolve a Promise, it is not so hard to do.
I modified your script quite drastically. Here is the result:
class Task {
constructor(id, time) {
this.id = id;
this.time = time;
}
run() {
console.log(this + ' launched.');
return new Promise(resolve => {
setTimeout(() => {
console.log(this + ' completed.');
resolve();
}, this.time);
});
}
toString() {
return `Task ${this.id}[${this.time}ms]`;
}
}
class TaskManager {
constructor(capacity) {
this.capacity = capacity;
this.waiting = [];
this.running = [];
}
push(tk) {
this.waiting.push(tk);
if (this.running.length < this.capacity) {
this.next();
} else {
console.log(tk + ' put on hold.');
}
}
next() {
const task = this.waiting.shift();
if (!task) {
if (!this.running.length) {
console.log("All done.");
}
return; // No new tasks
}
this.running.push(task);
const runningTask = task.run();
console.log("Currently running: " + this.running);
runningTask.then(() => {
this.running = this.running.filter(t => t !== task);
console.log("Currently running: " + this.running);
this.next();
});
}
}
const a = new Task('A', 100);
const b = new Task('B', 200);
const c = new Task('C', 400);
const d = new Task('D', 5000);
const e = new Task('E', 6000);
const manager = new TaskManager(3);
manager.push(a);
manager.push(b);
manager.push(c);
manager.push(d);
manager.push(e);
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);
for (var i = 0; i <= 5000; i++) {
if(i % 5 == 0){
setTimeout(function() {
console.log(i);
}, 5000);
}
else{
console.log(i);
}
}
I want that once it reaches to number 5 then wait 5 seconds before displaying number 6 in the console. How can I achieve this?
I hope You meant this:
it console.log's non divideable to 5 numbers and then waits 5 second and goes again.
const wait = ms => {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
const URL = [...Array(5000).keys()].map(n => n * 2);
const serial = [...Array(5000).keys()].map(n => n * 10);
(async (URL, serial) => {
for (let i = 1; i <= 5000; i++) {
if (i % 5 === 0) {
await wait(5000);
const url = 'http://localhost:3000/?URL='+URL[i]+'&serial='+serial[i];
console.log('Opening url:', url);
window.open(url, '_blank');
continue;
}
await wait(500);
console.log(i);
}
})(URL, serial);
javascript is single thread, try not using blocking as it will freeze your entire program.
SetTimeout and recursion can do the job for you
function print(i,total) {
if (i >= total) {
return;
}
else if (i % 5 === 0) {
setTimeout(function(){
console.log(i);
return print(i+1,total);
},5000)
} else {
console.log(i);
return print(i+1, total);
}
}
print(1,100)
function st(i1) {
setTimeout(function () {
console.log(i1)
},5000)
}
function aa(st) {
return new Promise((resolve => {
for(var i=1;i<=10;i++){
if(i==5){
console.log(i)
i=i+1;
resolve(st(i))
break;
}
console.log(i)
}
}))
}
async function bb() {
var ss=await aa(st)
}
bb()