简体   繁体   中英

Promise.all always returns an empty array

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)); 

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_)); 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM