react ejemplos ejemplo and javascript arrays ecmascript-6 es6-promise

javascript - ejemplos - react filter function



Filtrado de una matriz con una funciĆ³n que devuelve una promesa (8)

Dado

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

La longitud es 3 porque las promesas se devuelven, no los valores. ¿Hay una manera de filtrar la matriz con una función que devuelve una promesa?

Nota: para este ejemplo, fs.stat se ha reemplazado con setTimeout, consulte https://github.com/silenceisgolden/learn-esnext/blob/array-filter-async-function/tutorials/array-filter-with-async-function.js para el código específico.


Aquí hay una manera:

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

La función filterAsync toma una matriz y una función que debe devolver true o false o devolver una promesa que resuelve true o false , lo que pediste (casi, no sobrecargué el rechazo de la promesa porque creo que es una mala idea). Déjame saber si tienes alguna pregunta al respecto.

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>


Aquí hay una solución elegante de 2017 usando async / await:

Uso muy sencillo:

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

La función auxiliar (copia esto en tu página web):

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

Manifestación:

// 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) }

Incluso voy a lanzar un CodePen .


Como se mencionó en los comentarios, Array.prototype.filter es síncrono y, por lo tanto, no admite Promesas.

Dado que ahora puede (teóricamente) subclasificar los tipos incorporados con ES6, debería poder agregar su propio método asíncrono que envuelve la función de filtro existente:

Nota: He comentado la subclasificación, porque Babel todavía no la admite para Arrays

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


Para gente de escritura tipográfica (o es6 simplemente elimine la sintaxis de tipo)

function mapAsync<T, U>(array: T[], callbackfn: (value: T, index: number, array: T[]) => Promise<U>): Promise<U[]> { return Promise.all(array.map(callbackfn)); } async function filterAsync<T>(array: T[], callbackfn: (value: T, index: number, array: T[]) => Promise<boolean>): Promise<T[]> { const filterMap = await mapAsync(array, callbackfn); return array.filter((value, index) => filterMap[index]); }

es6

function mapAsync(array, callbackfn) { return Promise.all(array.map(callbackfn)); } async function filterAsync(array, callbackfn) { const filterMap = await mapAsync(array, callbackfn); return array.filter((value, index) => filterMap[index]); }


Promesa Reductor al rescate!

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

Los reductores son impresionantes. "Reducir mi problema a mi objetivo" parece ser una estrategia bastante buena para algo más complejo que lo que las herramientas simples resolverán para usted, es decir, filtrar una serie de cosas que no están disponibles de inmediato.


Tarde en el juego, pero como nadie más lo mencionó, Bluebird es compatible con Promise.map, que es mi opción para los filtros que requieren un procesamiento aysnc para la condición.

function filterAsync(arr) { return Promise.map(arr, num => { if (num === 3) return num; }) .filter(num => num !== undefined) }


Una forma válida de hacer esto (pero parece demasiado desordenado):

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

Una vez más, parece demasiado desordenado.


Una variante de @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([])) }

Tenga en cuenta que si (como en el caso actual) no tiene que preocuparse por que el filtro () tenga efectos secundarios que deben ser serializados, también puede hacerlo:

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