array.filter с функцией async не работает [дубликат]

int to hex:

int a = 72;

Console.WriteLine ("{0: X}", a);

hex to int:

int b = 0xB76;

Console.WriteLine (b);

16
задан ajklein 26 October 2015 в 22:00
поделиться

7 ответов

Как упоминалось в комментариях, 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 Demo

13
ответ дан CodingIntrigue 19 August 2018 в 10:52
поделиться
  • 1
    Это недопустимо, так как super() необходимо вызвать перед любым присваиванием this внутри constructor – Farzad YZ 21 April 2017 в 17:05
  • 2
    @FarzadYZ Реализация подкласса была просто примером. Вам не нужен конструктор с истинным подклассом, так как вы бы использовали базовый конструктор Array и не использовали свой собственный хранилище данных – CodingIntrigue 21 April 2017 в 17:13
  • 3
    Вы правы, я просто предупреждал людей, которые слепо копируют-вставляют принятый ответ :) – Farzad YZ 21 April 2017 в 17:15
  • 4
    @FarzadYZ Ах, хорошо, похоже, вы можете просто раскомментировать этот блок и заставить его работать ... – CodingIntrigue 21 April 2017 в 17:17

Допустимый способ сделать это (но кажется слишком запутанным):

let arr = [1,2,3];

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

async function check(num) {
  try {
    await filter(num);
    return true;
  } catch(err) {
    return false;
  }
}

(async function() {
  for( let num of arr ) {
    let res = await check(num);
    if(!res) {
      let index = arr.indexOf(num);
      arr.splice(index, 1);
    }
  }
})();

Опять же, кажется слишком запутанным.

0
ответ дан ajklein 19 August 2018 в 10:52
поделиться
  • 1
    FYI ключевыми словами async / await являются ES7 (Кандидат), а не ES6 – CodingIntrigue 27 October 2015 в 08:38

Promise Reducer на помощь!

[1, 2, 3, 4].reduce((op, n) => {
    return op.then(filteredNs => {
        return new Promise(resolve => {
            setTimeout(() => {
                if (n >= 3) {
                    console.log("Keeping", n);
                    resolve(filteredNs.concat(n))
                } else {
                    console.log("Dropping", n);
                    resolve(filteredNs);
                }
            }, 1000);
        });
    });
}, Promise.resolve([]))
.then(filteredNs => console.log(filteredNs));

Редукторы потрясающие. «Уменьшите мою проблему до моей цели», похоже, является довольно хорошей стратегией для чего-либо более сложного, чем простые решения для вас, т. Е. Фильтрация массива вещей, которые не все доступны сразу.

9
ответ дан Dan Ross 19 August 2018 в 10:52
поделиться

Это элегантное решение 2017 с использованием async / await:

Очень простое использование:

const results = await filter(myArray, async num => {
  await doAsyncStuff()
  return num > 2
})

Вспомогательная функция (скопируйте это на свою веб-страницу):

async function filter(arr, callback) {
  const fail = Symbol()
  return (await Promise.all(arr.map(async item => (await callback(item)) ? item : fail))).filter(i=>i!==fail)
}

Демонстрация:

// Async IIFE
(async function() {
  const myArray = [1, 2, 3, 4, 5]

  // This is exactly what you'd expect to write 
  const results = await filter(myArray, async num => {
    await doAsyncStuff()
    return num > 2
  })

  console.log(results)
})()


// Arbitrary asynchronous function
function doAsyncStuff() {
  return Promise.resolve()
}


// The helper function
async function filter(arr, callback) {
  const fail = Symbol()
  return (await Promise.all(arr.map(async item => (await callback(item)) ? item : fail))).filter(i=>i!==fail)
}

Я даже выброшу код CodePen .

14
ответ дан Gabe Rogan 19 August 2018 в 10:52
поделиться
  • 1
    Это имеет незначительную разницу в поведении с обычным Array.filter: если вы пытаетесь фильтровать массив таким образом, чтобы включал неопределенные элементы, вы потеряете их. Например. filter([1, 2, undefined, 3], (x) => x !== 1) вернет [2, 3], а не [2, undefined, 3], как и должно быть. – Tim Perry 4 December 2017 в 11:48
  • 2
    @TimPerry правильно, не стесняйтесь пересматривать ответ, поэтому что-то более разумное :) – Gabe Rogan 4 December 2017 в 17:27
  • 3
    Один из вариантов - вернуть значение Symbol сторожевого устройства вместо undefined. – Tamlyn 30 April 2018 в 14:55
  • 4
    @Tamlyn добавил Symbol sentinel, чтобы исправить ситуацию undefined :) – Gabe Rogan 30 April 2018 в 15:30

Вот способ:

var wait = ms => new Promise(resolve => setTimeout(resolve, ms));
var filter = num => wait(1).then(() => num == 3);

var filterAsync = (array, filter) =>
  Promise.all(array.map(entry => filter(entry)))
  .then(bits => array.filter(entry => bits.shift()));

filterAsync([1,2,3], filter)
.then(results => console.log(results.length))
.catch(e => console.error(e));

Функция filterAsync принимает массив и функцию, которые должны либо возвращать true, либо false или возвращать обещание, которое разрешает true или false, что вы просили (почти, я не перегружал обещание, потому что считаю, что это плохая идея). Дайте мне знать, если у вас есть какие-либо вопросы по этому поводу.

var wait = ms => new Promise(resolve => setTimeout(resolve, ms));
var filter = num => wait(1).then(() => num == 3);

var filterAsync = (array, filter) =>
  Promise.all(array.map(entry => filter(entry)))
  .then(bits => array.filter(entry => bits.shift()));

filterAsync([1,2,3], filter)
.then(results => console.log(results.length))
.catch(e => console.error(e));

var console = { log: msg => div.innerHTML += msg + "<br>",
                error: e => console.log(e +", "+ (e.lineNumber-25)) };
<div id="div"></div>

17
ответ дан jib 19 August 2018 в 10:52
поделиться

Вариант @ DanRoss's:

async function filterNums(arr) {
  return await arr.reduce(async (res, val) => {
    res = await res
    if (await filter(val)) {
      res.push(val)
    }
    return res
  }, Promise.resolve([]))
}

Обратите внимание, что если (как в текущем случае) вам не нужно беспокоиться о том, что filter () имеет побочные эффекты, которые необходимо сериализовать, вы можете также делают:

async function filterNums(arr) {
  return await arr.reduce(async (res, val) => {
    if (await filter(val)) {
      (await res).push(val)
    }
    return res
  }, Promise.resolve([]))
}
0
ответ дан shaunc 19 August 2018 в 10:52
поделиться

Поздно к игре, но поскольку никто больше не упомянул об этом, Bluebird поддерживает Promise.map, который является моим ходом для фильтров, требующих обработки aysnc для условия,

function filterAsync(arr) {
    return Promise.map(arr, num => {
        if (num === 3) return num;
    })
        .filter(num => num !== undefined)
}
1
ответ дан Spencer MacBeth 19 August 2018 в 10:52
поделиться
Другие вопросы по тегам:

Похожие вопросы: