4º. 1er cuatrimestre. Itinerario de Sistemas de la Información. Grado en Ingeniería Informática. ULL
Para la realización de esta práctica estudie /repase el tema Async Programming in JavaScript.
El objetivo es escribir un programa Node.js que usando fs.readFile
No se considera una solución usar fs.readFileSync
o timers (setTimeout
etc.) o usar promesas. Se pide una solución usando callbacks.
Use fs.readFile(path[, options], callback)
.
Este sería un ejemplo de uso:
$ concat -f one.txt -f two.txt -f three.txt -o salida.txt
Con commander es posible indicar una opción que se puede repetir
const program = require('commander');
function collect(value, previous) {
return previous.concat([value]);
}
program.option('-c, --collect <value>', 'repeatable value', collect, []);
program.parse(process.argv);
console.log(program.collect)
Ejecución:
$ node repeatable-option-commander.js -c a -c b -c c
[ 'a', 'b', 'c' ]
Lea la sección The Async Module de los apuntes y encuentre una solución usando Async
.
Considere la posibilidad de excepciones debidas a que alguno de los ficheros no exista.
Si no se le ocurre una solución, puede consultar las soluciones a la pregunta NodeJS - How to read multiple files asynchronously and write read contents to one file en StackOverflow.
A continuación, busque una solución para este problema sin hacer uso de Async
¿Cómo lo haría?
No se considera una solución usar fs.readFileSync
o timers (setTimeout
etc.) o usar promesas. Se pide una solución usando callbacks.
Haciendo abstracción de la solución encontrada en el paso anterior escriba una función asyncMap
que funcione como el map
del módulo Async
y que sirva
para cualuier función asíncrona que siga el patrón de callback(err, result)
:
asyncMap(inputs, (item, cb) => fs.readFile(item, cb), (err, contents) => { ... });
Ahora cambiamos el problema para lea en secuencial el conjunto de ficheros pasados como argumentos en línea de comandos y produzca como salida la concatenación de los mismos en el orden especificado. Las mismas restricciones que en el caso anterior.
Provea una función general series
que secuencialice cualquier array de funciones asíncronas.
Debe funcionar tal como lo hace la función series
del módulo Async.js.
Esta sería la forma de uso de la función series
:
series(program.files, (file, cb) => fs.readFile(file, cb), function(err, results) {
if (err == null) {
let file = fs.createWriteStream(program.output);
file.on('error', err => { throw new Error("Error en la apertura del archivo " + program.output + " " + err) });
results.forEach(i => { file.write(i + '\n'); });
file.end();
} else {
throw new Error("Fallo en la lectura de los ficheros\n" + err)
}
});
Esta es la estructura del template de la práctica:
➜ asyncmap-solution git:(main) tree -I node_modules
.
├── README.md
├── concatSerialize.js
├── my-async.mjs
├── package-lock.json
├── package.json
├── scripts
│ ├── create-inputs.bash
│ └── make-big-file.bash
├── sol-using-async.mjs
└── test
├── expected.txt
├── f1.txt
├── f2.txt
├── f3.txt
└── output.txt
En concatSerialize.js
los módulos son cargados usando require
(ver CommonJS) mientras que en sol-using-async.mjs
y my-async.mjs
se usan los módulos ES6 (ver ECMAScript Modules).
En el directorio scripts
hay dos scripts para la creación de ficheros de prueba y que son usados en la sección scripts
del package.json
.
➜ asyncmap-solution git:(main) npm run
Lifecycle scripts included in asyncmap-solution@1.0.0:
test
npm run clean; npm run create-inputs 3 7; npm run my-async.mjs; cmp --silent test/output.txt test/expected.txt && echo 'OK'
available via `npm run-script`:
create-inputs
scripts/create-inputs.bash ${npm_package_config_numfiles} ${npm_package_config_size}
my-async.mjs
node my-async.mjs -f test/f*.txt -o test/output.txt
sol-using-async.mjs
node sol-using-async.mjs -f test/f*.txt -o test/output.txt
concatSerialize.js
node concatSerialize.js -f test/f{1..3}.txt -o test/output.txt
test-err
node my-async.js -f f1.txt -f no-existe.txt -f f3.txt -o test/output.txt
save
git commit -am save && git push -u origin main
clean
rm -f test/f*.txt test/output.txt
El package.json
ilustra como se pueden definir variables en la sección "config"
y usarlas en los scripts
de npm
referenciándolas con ${npm_package_config_varname}
.:
...
"config": {
"numfiles": 3,
"size": 7
},
"scripts": {
...
"create-inputs": "scripts/create-inputs.bash ${npm_package_config_numfiles} ${npm_package_config_size}",
...
},
asyncmap
genérica que funciona como el map
de asyncseries
que resuelve el problema de serializar llamadas a funciones asíncronas