async/await dentro de las funciones de flecha (Array#map/filter)

5 minutos de lectura

Avatar de usuario de WelcomeTo
Bienvenido a

Recibo un error de tiempo de compilación en este código:

const someFunction = async (myArray) => {
    return myArray.map(myValue => {
        return {
            id: "my_id",
            myValue: await service.getByValue(myValue);
        }
    });
};

El mensaje de error es:

esperar es una palabra reservada

¿Por qué no puedo usarlo así?

  • No creo que puedas tener funciones de flecha asíncronas.

    – Puntiagudo

    27 de febrero de 2017 a las 15:49

  • Importante github.com/tc39/ecmascript-asyncawait/issues/7

    – Jared Smith

    27 de febrero de 2017 a las 15:51

  • Para resumir de la discusión de github vinculada, no puede hacer eso porque la función anónima que está pasando como devolución de llamada no es async y el interior await no puede afectar la función exterior.

    – Jared Smith

    27 de febrero de 2017 a las 15:55


  • async/await es parte de ES2017 (lanzamiento de este año), no de ES7 (lanzamiento del año pasado).

    – Félix Kling

    1 de marzo de 2017 a las 14:34

avatar de usuario de lonesomeday
algún día solitario

No puedes hacer esto como imaginas, porque no puedes usar await si no está directamente dentro de un async función.

Lo más sensato sería pasar la función a map asincrónico. Esto significa que map devolvería una serie de promesas. Entonces podemos usar Promise.all para obtener el resultado cuando todas las promesas regresen. Como Promise.all devuelve una promesa, la función externa no necesita ser async.

const someFunction = (myArray) => {
    const promises = myArray.map(async (myValue) => {
        return {
            id: "my_id",
            myValue: await service.getByValue(myValue)
        }
    });
    return Promise.all(promises);
}

  • en este caso, la matriz de promesas no es más lenta que la clásica para let in array?

    – stackdave

    3 de marzo de 2018 a las 6:38

  • @stackdave Probablemente, pero la diferencia será intrascendente al lado de service.getByValueque bien puede implicar una llamada de red…

    – algún día solitario

    3 de marzo de 2018 a las 15:23

  • gracias, comencé a usarlo, de todos modos, la legibilidad es mejor que la velocidad, porque la mayoría de las técnicas asincrónicas de ES6 siempre serán más lentas, pero a quién le importa

    – stackdave

    3 de marzo de 2018 a las 15:37

  • @lonesomeday ¿Puede esto manejar errores o limitaciones/retrasos entre llamadas?

    –Kyle Pennell

    25 de noviembre de 2019 a las 20:05

  • @KylePennell Sí. Necesitaría manejar los errores con un try..catch en la función asíncrona o con un catch controlador antes de regresar de la función externa. Se podría introducir un acelerador antes de que return en la función asíncrona.

    – algún día solitario

    30 de noviembre de 2019 a las 14:41

Si desea ejecutar map con una función de mapeo asíncrono, puede usar el siguiente código:

const resultArray = await Promise.all(inputArray.map(async (i) => someAsyncFunction(i)));

Cómo funciona:

  • inputArray.map(async ...) devuelve una matriz de promesas, una para cada valor en inputArray.
  • Poniendo Promise.all() alrededor de la matriz de promesas lo convierte en una sola promesa.
  • La única promesa de Promise.all() devuelve una matriz de valores: el individuo promete que cada uno se resuelve en un valor.
  • Nosotros ponemos await en frente de Promise.all() para que esperemos a que la promesa combinada se resuelva y almacene la matriz de subpromesas resueltas en la variable resultArray.

Al final obtenemos un valor de salida en resultArray para cada artículo en inputArraymapeada a través de la función someAsyncFunction. Tenemos que esperar a que se resuelvan todas las funciones asíncronas antes de que el resultado esté disponible.

avatar de usuario de helb
infierno

Eso es porque la función en map no es asíncronoasí que no puedes tener esperar en su declaración de devolución. Se compila con esta modificación:

const someFunction = async (myArray) => {
    return myArray.map(async (myValue) => { // <-- note the `async` on this line
        return {
            id: "my_id",
            myValue: await service.getByValue(myValue)
        }
    });
};

Pruébelo en Babel REPL

Entonces… no es posible dar una recomendación sin ver el resto de su aplicación, pero dependiendo de lo que esté tratando de hacer, haga la interno funcione de forma asíncrona o intente idear una arquitectura diferente para este bloque.

Actualización: es posible que obtengamos una espera de nivel superior algún día: https://github.com/MylesBorins/proposal-top-level-await

  • gracias, votado a favor, pero su código devuelve una matriz con objetos vacíos (es decir, [{}, {}]). Creo que necesito incluir en alguna parte awaitpero no podía darme cuenta de dónde

    – Bienvenido a

    27 de febrero de 2017 a las 16:13


  • Lo que hace el service.getByValue función parece?

    – ayuda

    27 de febrero de 2017 a las 16:19

  • simplemente devuelve ES6 Promise

    – Bienvenido a

    27 de febrero de 2017 a las 18:03

  • Me parece que el OP espera una serie de objetos identificados como resultado final, así que de acuerdo con eso, creo que probablemente quieras return await Promise.all(myArray.map… por equivalencia.

    – foque

    27 de febrero de 2017 a las 21:33


serán 2 instrucciones, pero solo cambie “esperar” con la instrucción adicional

let results = array.map((e) => fetch('....'))
results  = await Promise.all(results)

Intenté todas estas respuestas pero ninguna funciona para mi caso porque todas las respuestas devuelven un promise objeto no el result of the promise como esto:

{
  [[Prototype]]: Promise
  [[PromiseState]]: "fulfilled"
  [[PromiseResult]]: Array(3)
  0: ...an object data here...
  1: ...an object data here...
  2: ...an object data here...
  length: 3
  [[Prototype]]: Array(0)
}

Luego encontré esta respuesta https://stackoverflow.com/a/64978715/8339172 que establece si map la función no es asíncrona ni tiene en cuenta las promesas. Así que en lugar de usar await en el interior map función, yo uso for bucle y await el elemento individual porque dijo que for el bucle es async consciente y pausará el ciclo.

Cuando desee que se resuelva cada valor reasignado antes de pasar al siguiente, puede procesar la matriz como un iterable asíncrono.

A continuación, usamos la biblioteca iter-opspara reasignar cada valor en promesa, y luego producir un objeto con valor resuelto, porque map en sí mismo no debería manejar ninguna promesa internamente.

import {pipe, map, wait, toAsync} from 'iter-ops';

const i = pipe(
    toAsync(myArray), // make asynchronous
    map(myValue => {
        return service.getByValue(myValue).then(a => ({id: 'my_id', myValue: a}))
    }),
    wait() // wait for each promise
);

(async function() {
    for await (const a of i) {
        console.log(a); // print resulting objects
    }
})

Después de cada valor, usamos Espere para resolver cada valor reasignado a medida que se genera, para mantener el requisito de resolución consistente con la pregunta original.

¿Ha sido útil esta solución?