Reputation: 5135
I'm trying to solve this promise puzzle and I've had 2 questions:
A) I'm wondering why it's returning empty array. What am i doing wrong?
B) How can I implement async reduce?
B) How can I make it return a Async Array instead of empty array?
Note:
Please make use of
.get
method to iterate through the elements and the return value must be a asyncArray (not a regular array)
Code looks like this:
/**
* Async array.
*/
function AsyncArray(arr) {
this._arr = arr;
this.length = arr.length;
}
/**
* Asynchronously get the array item of the
* given index.
* @param {number} index - array index of the desired item
* @param {function} callback - called with the array item
*/
AsyncArray.prototype.get = function get(index, callback) {
setTimeout(callback, 0, this._arr[index]);
};
/**
* Async version of Array.prototype.map.
* @param {AsyncArray} arr
* @param {function} fn - (item: any) => any
* @returns {Promise<AsyncArray>}
*/
function asyncMap(arr, fn) {
let counter = 0; // counter
const res = []; /// array of promises.
const len = arr.length;
// Get the length.
return new Promise((resolve, reject) => { // Pending.
while(true) {
if(counter===len) {
console.log("before break", res);
break;
}
arr.get(counter, item => {
res[counter] = function() {
return new Promise((resolve, reject) => {
return resolve(fn(item));
});
}();
console.log('r',res);
});
counter += 1;
}
Promise.all(res).then((r1, rej) => {
console.log("hello world", r1);
return resolve(res);
});
});
}
/**
* Async version of Array.prototype.reduce.
* @param {AsyncArray} arr
* @param {function} fn - (val: any, item: any) => any
* @returns {Promise<any>}
*/
function asyncReduce(arr, fn, initVal) {}
const arr = new AsyncArray([1, 2, 3]);
// arr.get(1, item => console.log(item)); // Existing
// Expected result: [2, 4, 6];
asyncMap(arr, x => x * 2).then(arr_ => console.log('asyncMap:', arr_));
// Expected result: 106
// asyncReduce(arr, (v, x) => v + x, 100).then(val => console.log('asyncReduce:', val));
Upvotes: 2
Views: 3042
Reputation: 370729
You're putting the result of the get
calls into the results array asynchronously - res
is empty when you call Promise.all
on it:
arr.get(counter, item => {
res[counter] = function() {
return new Promise((resolve, reject) => {
return resolve(fn(item));
});
}();
});
// at this point, nothing has been pushed to res
Promise.all(res)
called synchronously after that point won't wait for anything, because none of the items in the array are Promises.
You could push a Promise to the array synchronously instead:
res.push(new Promise((resolve) => {
arr.get(counter, item => resolve(fn(item)));
}));
/**
* Async array.
*/
function AsyncArray(arr) {
this._arr = arr;
this.length = arr.length;
}
/**
* Asynchronously get the array item of the
* given index.
* @param {number} index - array index of the desired item
* @param {function} callback - called with the array item
*/
AsyncArray.prototype.get = function get(index, callback) {
setTimeout(callback, 0, this._arr[index]);
};
/**
* Async version of Array.prototype.map.
* @param {AsyncArray} arr
* @param {function} fn - (item: any) => any
* @returns {Promise<AsyncArray>}
*/
function asyncMap(arr, fn) {
let counter = 0; // counter
const res = []; /// array of promises.
const len = arr.length;
// Get the length.
return new Promise((resolve, reject) => { // Pending.
while(true) {
if(counter===len) {
console.log("before break", res);
break;
}
res.push(new Promise((resolve) => {
arr.get(counter, item => resolve(fn(item)));
}));
counter += 1;
}
Promise.all(res).then((r1, rej) => {
console.log("hello world", r1);
return resolve(r1);
});
});
}
/**
* Async version of Array.prototype.reduce.
* @param {AsyncArray} arr
* @param {function} fn - (val: any, item: any) => any
* @returns {Promise<any>}
*/
function asyncReduce(arr, fn, initVal) {}
const arr = new AsyncArray([1, 2, 3]);
// arr.get(1, item => console.log(item)); // Existing
// Expected result: [2, 4, 6];
asyncMap(arr, x => x * 2).then(arr_ => console.log('asyncMap:', arr_));
// Expected result: 106
// asyncReduce(arr, (v, x) => v + x, 100).then(val => console.log('asyncReduce:', val));
I'd prefer to use .map
on an array of the internal array's length to map each arr.get
call to a Promise in an array, and call Promise.all
on that array:
function AsyncArray(arr) {
this._arr = arr;
this.length = arr.length;
}
AsyncArray.prototype.get = function get(index, callback) {
setTimeout(callback, 0, this._arr[index]);
};
function asyncMap(asyncArr, fn) {
return Promise.all(
new Array(asyncArr.length).fill().map(
(item, i) => new Promise(resolve => asyncArr.get(i, item => resolve(fn(item))))
)
).then((result) => {
console.log("hello world", result);
return result;
});
}
const arr = new AsyncArray([1, 2, 3]);
asyncMap(arr, x => x * 2).then(arr_ => console.log('asyncMap:', arr_));
For async reduce, have the accumulator be a Promise that resolves to the array after having been pushed in the last iteration:
function asyncReduce(asyncArr, fn, initVal) {
return new Array(asyncArr.length).fill().reduce(
(a, _, i) => a.then(resultsArr => {
// feel free to use asynchronous operations here
return new Promise((resolve) => {
asyncArr.get(i, resultItem => {
resultsArr.push(fn(resultItem));
resolve(resultsArr);
});
});
}),
Promise.resolve(initVal)
);
}
function AsyncArray(arr) {
this._arr = arr;
this.length = arr.length;
}
AsyncArray.prototype.get = function get(index, callback) {
setTimeout(callback, 0, this._arr[index]);
};
const arr = new AsyncArray([1, 2, 3]);
asyncReduce(arr, x => x * 2, []).then(arr_ => console.log('asyncReduce:', arr_));
To also return instances of asyncArray
, just call new AsyncArray
before resolving, for example:
function asyncReduce(asyncArr, fn, initVal) {
return new Array(asyncArr.length).fill().reduce(
(a, _, i) => a.then(resultsArr => {
// feel free to use asynchronous operations here
return new Promise((resolve) => {
asyncArr.get(i, resultItem => {
resultsArr.push(fn(resultItem));
resolve(resultsArr);
});
});
}),
Promise.resolve(initVal)
)
.then((resultArr) => new AsyncArray(resultArr));
}
function AsyncArray(arr) {
this._arr = arr;
this.length = arr.length;
}
AsyncArray.prototype.get = function get(index, callback) {
setTimeout(callback, 0, this._arr[index]);
};
const arr = new AsyncArray([1, 2, 3]);
asyncReduce(arr, x => x * 2, []).then(arr_ => console.log('asyncReduce:', arr_));
Upvotes: 4