Javascript array itself turns undefined whilst iterating over it - javascript

I'm doing some javascript challenges on codeschool.com.
Whilst at the function expressions chapter, I came across the following weird bug.
In the following script - which can be heavily refactored, I know - the 'queue' variable gets assigned a value of 'undefined' after the second run.
First iteration:
queue array printed, containing 4 elements
"hello 1" printed
Second iteration:
queue array printed, containing 3 elements
"hello 3" printed
queue array printed, containing 2 elements
Third iteration - where the bug(?) occurs:
undefined is printed - instead of the queue array which should still contain 2 elements
First I thought something was wrong at codeschool's end, but after trying to run the code in chrome's dev tools I got the same results.
As far as I can tell nothing asynchronous should be happening which could mess anything up?
var puzzlers = [
function(a) { return 8 * a - 10; },
function(a) { return (a - 3) * (a - 3) * (a - 3); },
function(a) { return a * a + 4; },
function(a) { return a % 5; }
];
var start = 2;
var applyAndEmpty = function(input, queue) {
for (var i = 0; i < queue.length; i++) {
alert(queue);
if (i === 0) {
alert("hello 1");
var output = queue.shift()(input);
} else if (queue.length === 1) {
alert("hello 2");
return queue.shift()(output);
} else {
alert("hello 3");
output = queue.shift()(output);
alert(queue);
}
}
};
alert(applyAndEmpty(start, puzzlers));
Thanks!

Code Review
There's a couple bad things happening in this code; we should go over them first. For convenience's sake, I'm going to replace all alert calls with console.log calls – The console is much nicer for debugging things.
OK the first problem is you're using queue.shift() inside a for loop. Array.prototype.shift will change the length of the array, so you're not meant to use this inside a loop (outside of a very specialized case).
So each time we loop, i goes up one and queue.length goes down one – the two values are converging on each other which means you'll never actually touch all the values in queue
Refactor
We can fix that with a very simple adjustment of your function – remove the for loop! queue.shift() is effectively incrementing for us, but removing one element at a time. All we need to do is check if queue.length is empty – if so, we're done, otherwise shift one item off the queue and repeat
var applyAndEmpty = function(input, queue) {
console.log(input)
if (queue.length === 0)
return input;
else
return applyAndEmpty(queue.shift()(input), queue)
}
var puzzlers = [
function(a) { return 8 * a - 10; },
function(a) { return (a - 3) * (a - 3) * (a - 3); },
function(a) { return a * a + 4; },
function(a) { return a % 5; }
];
var start = 2;
console.log(applyAndEmpty(start, puzzlers));
// [initial input] 2
// 8 * 2 - 10 = 6
// (6 - 3) * (6 - 3) * (6 - 3) = 27
// 27 * 27 + 4 = 733
// 733 % 5 = 3
// [final output] = 3
console.log(puzzlers); // []
Greater lesson
There's one important thing to know about stepping thru arrays manually. You do ONE of the options below
1. check that the array is empty before attempting to get a value
// there are no items so we cannot get one
if (arr.length === 0)
doSomething;
// we know there is at least one item, so it is safe to get one
else
doOtherThing( arr.shift() )
2. null-check the thing you attempted to get
x = queue.shift();
// we didn't get a value, queue must've been empty
if (x === undefined)
doSomething
// yay we got something, we can use x now
else
doOtherThing( x )
It's your personal choice to use one over the other, tho I generally dislike null-checks of this kind. I believe it's better to check that the array has the value before attempting to grab it.
Doing BOTH of these options would be a logical error. If we check the array has a length of non-0, we can deduce we have an x – therefore we don't have to null-check for an x.
Remarks
I don't know if this was for an assignment or whatever, but you basically wrote up function composition from scratch – tho this is a kind of special destructive composition.
It seems you're aware of the destructive nature because you've named your function applyAndEmpty, but just in case you didn't know that was necessary, I'll share a non-destructive example below.
var lcompose = function(fs) {
return function (x) {
if (fs.length === 0)
return x
else
return lcompose(fs.slice(1))(fs[0](x))
}
}
var puzzlers = [
function(a) { return 8 * a - 10; },
function(a) { return (a - 3) * (a - 3) * (a - 3); },
function(a) { return a * a + 4; },
function(a) { return a % 5; }
];
var start = 2;
console.log(lcompose(puzzlers)(start)) // 3

That undefined you're getting is coming from this alert() call:
alert(applyAndEmpty(start, puzzlers));
At the end of the second iteration, i will be 1 and the queue length will be 2. Thus i is incremented for the next iteration, and the loop condition no longer holds — i is 2, and no longer less than the queue length.
It's a good habit when debugging to include some messaging instead of just values so that you can tell one debug output line from another. It's also a good idea to use console.log() instead of alert().

Assume the below solves your problem.
You used queue.length in second condition, at that stage, you perform only one shift and expecting a 4 size queue to be of 2 size, it is one error.
var puzzlers = [
function(a) { return 8 * a - 10; },
function(a) { return (a - 3) * (a - 3) * (a - 3); },
function(a) { return a * a + 4; },
function(a) { return a % 5; }
];
var start = 2;
var applyAndEmpty = function(input, queue) {
for (var i = 0; i < queue.length; i++) {
if (i === 0) {
alert("hello 1");
var output = queue.shift()(input);
} else if (i === 1) {
alert("hello 2");
return queue.shift()(output);
} else {
alert("hello 3");
output = queue.shift()(output);
}
}
};
alert(applyAndEmpty(start, puzzlers));

Related

Recursive approach to Persistent bugger problem returns undefined

I've been trying to solve the following problem in codewars using recursion:
Write a function, persistence, that takes in a positive parameter num and returns its multiplicative persistence, which is the number of times you must multiply the digits in num until you reach a single digit.
For example (Input --> Output):
39 --> 3 (because 3*9 = 27, 2*7 = 14, 1*4 = 4 and 4 has only one digit)
999 --> 4 (because 9*9*9 = 729, 7*2*9 = 126, 1*2*6 = 12, and finally 1*2 = 2)
4 --> 0 (because 4 is already a one-digit number)
Here's what I've tried:
var numOfIterations = 0;
function persistence(num) {
//code me
var i;
var digits=[];
var result = 1;
if (num.toString().length==1) {
return numOfIterations;
} else {
numOfIterations++;
digits = Array.from(String(num), Number);
for (i=0;i<digits.size;i++) {
result=result*digits[i];
}
persistence(result);
}
}
But for some reason, instead of returning the number of iterations, it returns undefined. I've been told that I'm not using recursion correctly, but I just can't find the problem.
Other answers have explained what's wrong with your code. I just want to point out a simpler implementation:
const multiplyDigits = (n) =>
n < 10 ? n : (n % 10) * multiplyDigits (n / 10 | 0);
const persistence = (n) =>
n < 10 ? 0 : 1 + persistence (multiplyDigits (n));
[39, 999, 4] .forEach (t => console .log (`${t}:\t${persistence (t)}`));
multiplyDigits does just what it says, recursively multiplying the final digit by the number left when you remove that last digit (Think of | 0 as like Math .floor), and stopping when n is a single digit.
persistence checks to see if we're already a single digit, and if so, returns zero. If not, we add one to the value we get when we recur on the multiple of the digits.
I've been told that I'm not using recursion correctly
You're recursing, but you're not returning the result of that recursion. Imagine for a moment just this structure:
function someFunc() {
if (someCondition) {
return 1;
} else {
anotherFunc();
}
}
If someCondition is false, what does someFunc() return? Nothing. So it's result is undefined.
Regardless of any recursion, at its simplest if you want to return a result from a function then you need to return it:
function persistence(num) {
//...
if (num.toString().length==1) {
//...
} else {
//...
return persistence(result); // <--- here
}
}
As #David wrote in his answer, you were missing the return of the recursive call to itself.
Plus you were using digits.size instead of digits.length.
Anyway consider that one single digit being zero will collpse the game because that's enough to set the result to zero despite how many digits the number is made of.
To deal with the reset of numOfIterations, at first I tried using function.caller to discriminate between recursive call and direct call and set the variable accordingly. Since that method is deprecated as shown here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/caller
I opted for the optional argument iteration that gets set to zero as default, to keep track of that value while it goes down the call stack. This solution still fulfills the fact that the caller doesn't need to know a new interface for the function to work.
//var numOfIterations = 0;
function persistence(num, iteration=0) {
/*
Commented strategy using the function.caller
working but deprecated so I can't recommend anymore
used optional argument iteration instead
//gets the name of the caller scope
let callerName = persistence.caller?.name;
//if it's different from the name of this function
if (callerName !== 'persistence')
//reset the numOfIterations
numOfIterations = 0;
*/
var digits=[];
if (num.toString().length==1){
return iteration;
} else {
var result = 1;
digits = Array.from(String(num), Number);
for (let i=0;i<digits.length;i++) {
result = result * digits[i];
}
return persistence(result, iteration+1);
}
}
console.log( persistence(39) ); //-> 3
console.log( persistence(999 ) ); //-> 4
console.log( persistence(4) ); //-> 0
You can do something like this
const persistenceTailRecursive = (num, iterations = 0) => {
const str = '' + num;
if(str.length === 1){
return iterations;
}
return persistenceTailRecursive(str.split('').reduce((res, a) => res * parseInt(a), 1), iterations + 1)
}
const persistence = (num) => {
const str = '' + num;
if(str.length === 1){
return 0;
}
return 1 + persistence(str.split('').reduce((res, a) => res * parseInt(a), 1))
}
console.log(persistenceTailRecursive(93))
console.log(persistenceTailRecursive(999))
console.log(persistence(93))
console.log(persistence(999))
There are 2 versions
1 tailRecursive call the same method with the exact signature (preventing stackoverflow in some languages like scala)
2 basic the result is calculated at the end

Double countdown using recursion function?

I'm trying to write a recursion using JavaScript, without using global variables but i am not sure that the program can be possible like desire output.
Question is "Given a non-negative integer n, create a double countdown-pattern in the output."
Example:
If you ran doubleCountdown(5) you would also get the following output:
5
4
3
2
1
4
3
2
1
0
I have tried following ways:
In this way getting down to up output but not exact output:
double_countdown(5);
function double_countdown(n){
if(n == 0){
console.log(n);
return
}
console.log(n);
double_countdown(n-1);
console.log(n);
}
In this ways, i have used global for desired output but not standard way.
let second_n = 0;
let double_count =0;
double_countdown(5);
function double_countdown(n){
if(n == 0 && double_count == 0){
double_count = 1;
double_countdown(second_n-1);
console.log(n);
}
if(n == 0){
return
}else{
second_n +=1;
}
console.log(n);
double_countdown(n-1);
}
I would write the single countdown as a recursive function and have a double countdown function simply call it twice. This keeps all the parts simple.
const countdown = (s, e = 0) =>
s < e ? [] : [s, ...countdown (s -1, e)]
const doubleCountdown = (n) =>
[...countdown (n, 1), ...countdown (n - 1, 0)]
console .log (doubleCountdown (5))
You can then log the answer however you want for your homework. Perhaps something like for (let n of doubleCountdown (5)) {console .log (n)}
Implement the recursive function in a way that it keeps track of its recursion state by providing and working with more parameters than just the required first parameter (which is the current count down value).
Thus one would implement e.g. a function which is capable of running the count down cycle N times. And a double_countdown respectively countDownTwice function could be derived from such a generic implementation.
function countDownNTimes(value = 0, times = 1, initialValue) {
// initially invoked with an `undefined` value for `initialValue`.
initialValue ??= value;
console.log(value);
if (times >= 2) {
if (value <= 1) {
// does trigger a new recursion cycle with minimized boundaries.
countDownNTimes(initialValue - 1, --times, initialValue);
} else {
// stays within the current recursion of minimized boundaries.
countDownNTimes(--value, times, initialValue);
}
} else if ((times >= 1) && (value >= 1)) {
// stays within the current, final recursion of reastablished boundaries.
countDownNTimes(--value, times, initialValue);
}
}
const countDownTwice = value => countDownNTimes(value, 2);
console.log('... countDownTwice(5) ...');
countDownTwice(5);
console.log('... countDownNTimes(3, 3) ...');
countDownNTimes(3, 3);
.as-console-wrapper { min-height: 100%!important; top: 0; }
Finally, after trying many times. I got the answer. If anything wrong then please help for more improve.
double_countdown(5);
function double_countdown(n,sc=0){
if(sc == 0) sc = n;
if(n == 0 ) return ;
console.log(n);
double_countdown(n-1,sc);
console.log(sc-n);
}

JS HackerRank Jesse and Cookies - Heap problem times out

I am trying to solve the Hackerrank problem Jesse and Cookies:
Jesse loves cookies and wants the sweetness of some cookies to be greater than value 𝑘. To do this, two cookies with the least sweetness are repeatedly mixed. This creates a special combined cookie with:
sweetness = (1 × Least sweet cookie + 2 × 2nd least sweet cookie).
This occurs until all the cookies have a sweetness ≥ 𝑘.
Given the sweetness of a number of cookies, determine the minimum number of operations required. If it is not possible, return −1.
Example
k = 9
A = [2,7,3,6,4,6]
The smallest values are 2, 3.
Remove them then return 2 + 2 × 3 = 8 to the array. Now A = [8,7,6,4,6].
Remove 4, 6 and return 4 + 2 × 6 = 16 to the array. Now A = [16,8,7,6].
Remove 6, 7, return 6 + 2 × 7 = 20 and A = [20,16,8,7].
Finally, remove 8, 7 and return 7 + 2 × 8 = 23 to A. Now A = [23,20,16].
All values are ≥ 𝑘 = 9 so the process stops after 4 iterations. Return 4.
I couldn't find a JavaScript solution or a hint for this problem. My code seems to be working, except that it times out for a large array (input size > 1 million).
Is there a way to make my code more efficient? I think the time complexity is between linear and O(n log n).
My Code:
function cookies(k, A) {
A.sort((a,b)=>a-b)
let ops = 0;
while (A[0] < k && A.length > 1) {
ops++;
let calc = (A[0] * 1) + (A[1] * 2);
A.splice(0, 2);
let inserted = false
if (A.length === 0) { // when the array is empty after splice
A.push(calc);
} else {
for (var i = 0; i < A.length && !inserted; i++) {
if (A[A.length - 1] < calc) {
A.push(calc)
inserted = true
} else if (A[i] >= calc) {
A.splice(i, 0, calc);
inserted = true
}
}
}
}
if (A[0] < k) {
ops = -1;
}
return ops;
}
It is indeed a problem that can be solved efficiently with a heap. As JavaScript has no native heap, just implement your own.
You should also cope with inputs that are huge, but where most values are greater than k. Those should not have to be part of the heap -- it would just make heap operations unnecessarily slower. Also, when cookies are augmented, they only need to enter back into the heap when they are not yet good enough.
Special care needs to be taken when the heap ends up with just one value (less than k). In that case it needs to be checked whether any good cookies were created (and thus did not end up in the heap). If so, then with one more operation the solution has been found. But if not, it means there is no solution and -1 should be returned.
Here is an implementation in JavaScript:
/* MinHeap implementation without payload. */
const MinHeap = {
/* siftDown:
* The node at the given index of the given heap is sifted down in its subtree
* until it does not have a child with a lesser value.
*/
siftDown(arr, i=0, value=arr[i]) {
if (i >= arr.length) return;
while (true) {
// Choose the child with the least value
let j = i*2+1;
if (j+1 < arr.length && arr[j] > arr[j+1]) j++;
// If no child has lesser value, then we've found the spot!
if (j >= arr.length || value <= arr[j]) break;
// Move the selected child value one level up...
arr[i] = arr[j];
// ...and consider the child slot for putting our sifted value
i = j;
}
arr[i] = value; // Place the sifted value at the found spot
},
/* heapify:
* The given array is reordered in-place so that it becomes a valid heap.
* Elements in the given array must have a [0] property (e.g. arrays). That [0] value
* serves as the key to establish the heap order. The rest of such an element is just payload.
* It also returns the heap.
*/
heapify(arr) {
// Establish heap with an incremental, bottom-up process
for (let i = arr.length>>1; i--; ) this.siftDown(arr, i);
return arr;
},
/* pop:
* Extracts the root of the given heap, and returns it (the subarray).
* Returns undefined if the heap is empty
*/
pop(arr) {
// Pop the last leaf from the given heap, and exchange it with its root
return this.exchange(arr, arr.pop());
},
/* exchange:
* Replaces the root node of the given heap with the given node, and returns the previous root.
* Returns the given node if the heap is empty.
* This is similar to a call of pop and push, but is more efficient.
*/
exchange(arr, value) {
if (!arr.length) return value;
// Get the root node, so to return it later
let oldValue = arr[0];
// Inject the replacing node using the sift-down process
this.siftDown(arr, 0, value);
return oldValue;
},
/* push:
* Inserts the given node into the given heap. It returns the heap.
*/
push(arr, value) {
// First assume the insertion spot is at the very end (as a leaf)
let i = arr.length;
let j;
// Then follow the path to the root, moving values down for as long as they
// are greater than the value to be inserted
while ((j = (i-1)>>1) >= 0 && value < arr[j]) {
arr[i] = arr[j];
i = j;
}
// Found the insertion spot
arr[i] = value;
return arr;
}
};
function cookies(k, arr) {
// Remove values that are already OK so to keep heap size minimal
const heap = arr.filter(val => val < k);
let greaterPresent = heap.length < arr.length; // Mark whether there is a good cookie
MinHeap.heapify(heap);
let result = 0;
while (heap.length > 1) {
const newValue = MinHeap.pop(heap) + MinHeap.pop(heap) * 2;
// Only push result back to heap if it still is not great enough
if (newValue < k) MinHeap.push(heap, newValue);
else greaterPresent = true; // Otherwise just mark that we have a good cookie
result++;
}
// If not good cookies were created, then return -1
// Otherwise, if there is still 1 element in the heap, add 1
return greaterPresent ? result + heap.length : -1;
}
// Example run
console.log(cookies(9, [2,7,3,6,4,6])); // 4
I solved it using java. You may adapt to Javascript.
This code does not require using a heap. It just work on the same array passed. Passed all tests for me.
static int cookies(int k, int[] arr) {
/*
* Write your code here.
*/
Arrays.sort(arr);
int i = 0,
c = arr.length,
i0 = 0,
c0 = 0,
op = 0;
while( (arr[i]<k || arr[i0]<k) && (c0-i0 + c-i)>1 ) {
int s1 = i0==c0 || arr[i]<=arr[i0] ? arr[i++] : arr[i0++],
s2 = i0==c0 || (i!=c && arr[i]<=arr[i0]) ? arr[i++] : arr[i0++];
arr[c0++] = s1 + 2*s2;
op++;
if( i==c ) {
i = i0;
c = c0;
c0 = i0;
}
}
return c-i>1 || arr[i]>=k ? op : -1;
}
First of all sort array.
For newly calculated values, store them in the array[i0-c0] range, this new array does not require sorting, because it is already sorted.
When array[i-c] reaches(i==c: true) end, forget it, and work on arr[i0-c0].

Taking apart a Function Expression - Struggling with a Couple Lines/Parameters

So I'm looking at a piece of code that is confusing me despite reading an explanation or two:
Here's the code..
var puzzlers = [
function (a) { return 8*a - 10; },
function (a) { return (a-3) * (a-3) * (a-3); },
function (a) { return a * a + 4; },
function (a) { return a % 5; }
];
var start = 2;
var applyAndEmpty = function (input, queue) {
var length = queue.length;
for (var i = 0; i < length; i++) {
input = queue.shift()(input);
}
return input;
};
alert(applyAndEmpty(start, puzzlers));
I understand most of it but a breakdown would be great, what really boggles me is the beginning and end of this line input = queue.shift()(input); I know it's using input to store the outcome, but why? and why is there an input parameter on the end as well?
PS This line alert(applyAndEmpty(start, puzzlers)); I know calls the function then alerts it. Why do I have to call a function before I can alert/console log etc it? Is this because it isn't IIFE and so theres nothing to actually alert until the function has been called? Its quite like an 'on' button?
Sorry this is long, thanks in advance!
I've slightly edited the code in the for loop for clarity.
// This array contains 5 items, each item is a function which takes a single param and returns a number.
var puzzlers = [
function (a) { return 8*a - 10; },
function (a) { return (a-3) * (a-3) * (a-3); },
function (a) { return a * a + 4; },
function (a) { return a % 5; }
];
// The start value is 2.
var start = 2;
var applyAndEmpty = function (input, queue) {
// Get the number of items in the queue.
var length = queue.length;
// Iterate over all the items in the queue.
for (var i = 0; i < length; i++) {
// Remove the item at index 0 from the queue, the item is stored in the var.
var itemMethod = queue.shift();
// Execute the method, pass it the current value as input param. The result
// of the method will be placed back into the input variable.
input = itemMethod(input);
}
// Return the modified input value.
return input;
};
// Runs the applyAndEmpty method and shows the output in an alert.
alert(applyAndEmpty(start, puzzlers));
// The breakdown of the for loop:
// i = 0, input = 2 -> return 8 * 2 - 10 = 6
// i = 1, input = 6 -> return (6-3) * (6-3) * (6-3) = 27
// i = 2, input = 27 -> return 27 * 27 + 4 = 733
// i = 3, input = 733 -> return 733 % 5 = 3
// And thus the alert says three.
If you don't place the result of the current itemMethod back into input it means you will call each method from puzzlers with the value 2. The result of applyAndEmpty would no longer be 3 but just be 2 as the input variable is never changed. So if you don't store the result of calling the puzzler methods you might as well skip them altogether and just return the input param immediately.
It's just a way to chain the functions in the array, so that the result from the first function is passed to the second function, the result from the second function is passed to the third function, and so on...
f = [ a => 8 * a - 10,
a => (a-3) * (a-3) * (a-3),
a => a * a + 4,
a => a % 5 ]
console.log(f[3](f[2](f[1](f[0](2))))) // 3
console.log(f.pop()(f.pop()(f.pop()(f.pop()(2))))) // 3
console.log((a => a % 5)((a => a * a + 4)((a => (a-3) * (a-3) * (a-3))((a => 8*a - 10)(2))))) // 3

Javascript pattern to break up long-running recursive function

I have a long-running function that does a huge calculation: all the possible permutations of x n-sided dice and the probability of those outcomes. For small x and n, the calculation is quick. For large values (n = 100, x > 3), the calculation takes tens of seconds if not longer; meanwhile, the browser stalls.
Here's a snippet of my code:
let dist = [];
// min and max are the minimum and maximum possible values
// (if dice are all 1's or all their maximum values)
for (let i = min; i <= max; i++) {
// initialize possible values of our distribution to 0
dist.push([ i, 0 ]);
}
// total is the total outcome value so far
// dIndex is the index into the dice-array (diceList) for the die we're
// currently applying to our total--the die we're "rolling"
function applyDie(total, dIndex) {
if (dIndex === diceList.length) {
dist[total - min][1]++;
permutationsCount++;
return;
}
// diceList is an array of integers representing the number of sides
// for each die (one array element = one die of element-value sides)
for (let i = 1; i <= diceList[dIndex]; i++) {
applyDie(total + i, dIndex + 1);
}
}
// kick off recursive call
applyDie(0, 0);
I want to add two functionalities:
Cancellation
Progress reporting
Cancellation will be easy (I can do it myself) once I have the async pattern in place, so I really only need help with progress reporting, or, more accurately, simply breaking the recursive pattern into chunks based on the permutationsCount variable. i.e.
/* ... */
permutationsCount++;
if (permutationsCount % chunkSize === 0)
/* end this chunk and start a new one */
I would prefer to use Javasciprt Promises, but I'm open to other suggestions.
Ideas?
Here's a function I wrote to do something similar. It's for a calculation done entirely in javascript... I couldn't tell from your question whether you were working entirely on the client side or what.
// Break the list up into equal-sized chunks, applying f() to each item of
// the list, writing a %-complete value to the progress span after each
// chunk. Then execute a callback with the resulting data.
var chunkedLoop = function (list, f, progressSpan, callback) {
var numChunks = 10,
chunkSize = Math.round(list.length / numChunks),
chunkedList = [], // will be a list of lists
// Concatenated results of all chunks, passed to the callback.
resultList = [],
x, // just a loop variable
chunkIndex = 0; // tracks the current chunk across timeouts
progressSpan.html(0);
// Splice of chunks of equal size, but allow the last one to be of an
// arbitrary size, in case numChunks doesn't divide the length of the
// list evenly.
for (x = 0; x < numChunks - 1; x += 1) {
chunkedList.push(list.splice(0, chunkSize));
}
chunkedList.push(list);
// Schedule a series of timeouts, one for each chunk. The browser
// stops blocking for a moment between each chunk, allowing the screen
// to update. This is the only way to have progress values reported to
// the view mid-loop. If it was done in a single loop, execution would
// block all the way to the end, and the screen would only update once
// at 100%.
var chunkFunction = function () {
setTimeout(function () {
// Run f on the chunk.
var chunk = chunkedList[chunkIndex];
var r = forEach(chunk, f);
resultList = resultList.concat(r);
chunkIndex += 1;
// Update progress on the screen.
progressSpan.html(Math.round(chunkIndex / numChunks * 100));
// Schedule the next run, if this isn't the last chunk. If it
// is the last chunk, execute the callback with the results.
if (chunkIndex < chunkedList.length) {
chunkFunction();
} else if (callback instanceof Function) {
callback.call(undefined, resultList);
}
// There's no reason to delay more than the minimum one
// millisecond, since the point is just to break up javascript's
// single-threaded blocking.
}, 1);
};
chunkFunction();
};
For reporting status you can pass callback function into your recursuve function and do there whatever you like (increase counter, push status into page etc).
Also think about rewriting recursive into iterative algorithm because it will have lesser memory overhead and it will be much easier to put there some other logic (like cancellations you mentioned)
You could use setTimeout to let JavaScript do other things and unstuck the event loop. This way even infinite loop would nonblocking. Here is a quick dirty example.
http://jsfiddle.net/xx5adut6/
function isPrime(n) {
// If n is less than 2 or not an integer then by definition cannot be prime.
if (n < 2) {
return false
}
if (n != Math.round(n)) {
return false
}
var isPrime = true;
for (var i = 2; i <= Math.sqrt(n); i++) {
if (n % i == 0) {
isPrime = false
}
}
// Finally return whether n is prime or not.
return isPrime;
}
var cancel = false;
var i = 0;
var primesFound = 0;
var status = $('.status');
var timeout;
function loop4Primes() {
if (cancel) {
clearTimeout(timeout);
return;
}
if (isPrime(i++)) primesFound++;
timeout = setTimeout(loop4Primes, 1);
}
function updateText() {
status.text(primesFound);
}
var interval = setInterval(updateText, 1000);
$('#start').click(function () {
loop4Primes();
$(this).off('click');
});
$('#stop').click(function () {
clearInterval(interval);
updateText();
cancel = true;
});

Categories

Resources