Cómo habilitar async.mapLimit para que funcione con TypeScript async/await
¿Es posible usar el módulo Async de NPM para trabajar con async/await en TypeScript 2.2x?
Objetivo: Crear un scraper web que ejecute 10 solicitudes HTTP en paralelo usando la función mapLimit de Async.
Un ejemplo de la función HTTP envuelta sería el siguiente:
async callUniRest(url: string): Promise<number> {
return new Promise<number>(resolve => {
unirest.get(url)
.end((response: any) => {
resolve(cheerio.load(response.body);
});
});
}
Problema:
Cuando corro:
const myList: string[] = ['http...', 'http...', 'http...', 'http...']
async.mapLimit(myList, 10, callUniRest, function(err: any, results: any {
console.log(results);
})
La devolución de llamada solo se ejecuta después de que el primer elemento termina.
Pregunta: ¿Cómo habilito async.mapLimit para que funcione con múltiples llamadas?
Respuesta #1
¿Es posible usar el módulo npm 'async' para trabajar con async/await en Typescript?
Sí.
Descubrí que mapLimit solo estaba llamando al iterador para el primer conjunto. Esto se debía a que estaba transpilando desde TypeScript. Si usaba async/await nativo con JS, entonces funcionaba. Esto se debe a, citado de la documentación de async para AsyncFunction:
debido a las limitaciones de JavaScript, solo podemos detectar funciones asincrónicas nativas y no implementaciones transpiladas.
Entonces, si envuelves tu iterador con asyncify de la biblioteca async, entonces puedes retornar desde dentro de tu iterador y no usar un callback:
Si estás utilizando funciones asíncronas a través de un transpilador (por ejemplo, Babel), aún debes envolver la función con asyncify, porque la función asíncrona se compilará en una función ordinaria que devuelve una promesa.
Aquí hay un ejemplo de TypeScript algo forzado que demuestra cómo hacer esto. Nota: si eliminas la llamada a asyncify, no funcionará:
import { asyncify, mapLimit } from "async";
const listOfThings = "abcdefghijklmnopqrstuvwxyz".split("");
const convertToUppercase = async () =>
await mapLimit<string, string[]>(
listOfThings,
5,
asyncify(async letter => letter.toUpperCase())
);
const init = async () => {
const uppered = await convertToUppercase();
console.log("done", uppered);
};
init();
Respuesta #2
No. El módulo async proporciona utilidades y abstracciones para personas que no pueden o no quieren usar Promises. No se mezclan.
Sin mencionar que, para cuando tengas myList, todas las solicitudes ya se habían enviado.
async.mapLimit solo funciona con funciones que aceptan un callback, y no con las que devuelven una Promesa.
Lo que podrías querer es usar Promise.map() de Bluebird con el operador concurrency
