1. ホーム
  2. javascript

[解決済み] プロミスを返す関数で配列をフィルタリングする

2023-05-22 04:18:25

質問

与えられた

let arr = [1,2,3];

function filter(num) {
  return new Promise((res, rej) => {
    setTimeout(() => {
      if( num === 3 ) {
        res(num);
      } else {
        rej();
      }
    }, 1);
  });
 }

 function filterNums() {
   return Promise.all(arr.filter(filter));
 }

 filterNums().then(results => {
   let l = results.length;
   // length should be 1, but is 3
 });

値ではなくPromiseが返されるため、長さが3になっています。Promiseを返す関数で配列にフィルタをかける方法はありますか?

注:この例では、fs.statはsetTimeoutに置き換えられており、以下を参照してください。 https://github.com/silenceisgolden/learn-esnext/blob/array-filter-async-function/tutorials/array-filter-with-async-function.js を参照してください。

どのように解決するのですか?

コメントにもあるように Array.prototype.filter 同期であるため、Promisesをサポートしていません。

ES6で組み込み型をサブクラス化できるようになったので、既存のフィルタ関数をラップした独自の非同期メソッドを追加することができるはずです。

注意: サブクラス化をコメントアウトしています。

class AsyncArray /*extends Array*/ {
  constructor(arr) {
    this.data = arr; // In place of Array subclassing
  }

  filterAsync(predicate) {
     // Take a copy of the array, it might mutate by the time we've finished
    const data = Array.from(this.data);
    // Transform all the elements into an array of promises using the predicate
    // as the promise
    return Promise.all(data.map((element, index) => predicate(element, index, data)))
    // Use the result of the promises to call the underlying sync filter function
      .then(result => {
        return data.filter((element, index) => {
          return result[index];
        });
      });
  }
}
// Create an instance of your subclass instead
let arr = new AsyncArray([1,2,3,4,5]);
// Pass in your own predicate
arr.filterAsync(async (element) => {
  return new Promise(res => {
    setTimeout(() => {
      res(element > 3);
    }, 1);
  });
}).then(result => {
  console.log(result)
});

Babel REPL デモ