5. hét
Promise, async, error first callback
Pár izgalmasabb javascript / node specifikus dolog.
A nyelvi elemek előadásra épít erősen...
module.exports = function (objectrepository) {
return function (req, res, next) {
userModel.findOne({}, function (err,results){
res.tpl.users = results;
return next();
});
return next();
};
};
A function nagyon gyakran használt nyelvi elem.
fs.readFile('config.json',
function (error, text) {
if (error) {
console.error('Error while reading config file');
} else {
try {
const obj = JSON.parse(text);
console.log(JSON.stringify(obj, null, 4));
} catch (e) {
console.error('Invalid JSON in file');
}
}
});
"Sima" aszinkron és szinkron funkciók egymás után.
Használjunk promiset!
readFilePromisified('config.json')
.then(JSON.parse)
.then(function (obj) {
console.log(JSON.stringify(obj, null, 4));
})
.catch(function (reason) {
console.error('An error occurred', reason);
});
Egy oszályba burkolt függvény, ami az állapotváltását láncolható módon tájékoztatja a rá feliratkozókat. ES6-ban natív nyelvi elem!!
const promise = new Promise(
function (resolve, reject) {
...
if (...) {
resolve(value);
} else {
reject(reason);
}
});
promise
.then(
function(a){console.log('Resolve:' + a);},
function(b){console.log('Reject:' + b);})
.catch(function(e){console.error(e);});
Siker esetén resolve, hiba esetén reject. Ha egyiket sem hívod, akkor végtelenségig vár!
és dobott kivételeket is lehet kezelni (ha nincs reject ág then-ben, akkor a catchig jut el a hibakezelés)
Láncolhatóak ha egy függvény promiset ad vissza, vagy ha szinkron.
Ha bármelyik rejectet hív, megtörik a lánc.
function promiseFv(item){
return new Promise(function (resolve, reject) {
resolve(item+'p');
});}
function promiseFv(item) {
return new Promise(function (resolve, reject) {
resolve(item + 'p');
});
}
function normalFv(item) { return item + 'n'; }
promiseFv('1')
.then(normalFv)
.then(promiseFv)
.then(function (item) {return item + '2';})
.then(console.log)
.catch(function (e) {
console.log('error:' + e);
});
Ezt írja ki: 1pnp2
Ha a láncban bárhol hiba dobódik, akkor az az első catch-ig fog elérni.
Elég egy catch, kevesebb hibakezelési ág, ez jó!
Több promise egy várakozásban összefogva - vagy megvárja mindegyik sikerét, vagy az első kudarcot.
const p1 = Promise.resolve(3);
const p2 = 1337;
const p3 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, "foo");
});
Promise.all([p1, p2, p3]).then(function(values) {
console.log(values); // [3, 1337, "foo"]
});
Több promise, amelyik gyorsabban befejeződik, az adódik át a thenben (a többi is fut / lefut, nem szakítódik meg).
const p1 = new Promise(function(resolve, reject) {
setTimeout(resolve, 500, "one");
});
const p2 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, "two");
});
Promise.race([p1, p2]).then(function(value) {
console.log(value); // "two"
// Both resolve, but p2 is faster
});
Szinkron function / érték becsomagolása promiseba, van ennek értelme. :)
function foo(){
return Promise.resolve('alma');
}
function bar(){
return Promise.reject('alma');
}
foo.then(bar).catch(console.log);
async function foo(){
//...
try {
await bar();
} catch(e){
// bar reject handle
}
}
async kontextus promiset ad vissza, await kulcsszó használható
async function foo (){
//...
}
foo().then(e=>console.log(e)).catch(err=>console.log(err));
Tényleg egy promise!
fs.readFile('config.json',
function (error, text) {
if (error) {
console.error('Error while reading config file');
} else {
try {
const obj = JSON.parse(text);
console.log(JSON.stringify(obj, null, 4));
} catch (e) {
console.error('Invalid JSON in file');
}
}
});
const fsPromise = require('fs').promises;
fsPromise.readFile('config.json')
.then(JSON.parse)
.then(function (obj) {
console.log(JSON.stringify(obj, null, 4));
})
.catch(function (reason) {
console.error(['An error occurred', reason]);
});
const fsPromise = require('fs').promises;
try {
JSON.stringify(JSON.parse(
await fsPromise.readFile('config.json'))
, null, 4);
} catch(err) {
console.error(['An error occurred', err]);
}
Bizonyos esetekben ez kell, bizonyos esetekben normál callbackek, illetve beszéljünk az async npm modulról.
npm i async
Listákon végrehajtott tömeges műveletek, control flow műveletek, általános async műveletek - szinte mindenre van valamilyen megérhető recept.
const lista = [1,2,3];
async.each(lista, function(item, callback) {
console.log('Processing:'+item);
setTimeout(function(){ console.log('Processing done:'+item);
callback(null); },Math.random()*1000);
}, function (err){ console.log('All done'); });
Eredmény:
Processing:1
Processing:2
Processing:3
Processing done:3
Processing done:1
Processing done:2
All done
map(arr, iterator, [callback])
const lista = [1,2,3];
async.map(lista, function (item, callback) {
callback(null, item * item);
}, function (err, results) {
console.log(results);
});
Eredmény:
[ 1, 4, 9 ]
series(tasks, [callback])
async.series([
function(callback){
callback(null, 'one');
},
function(callback){
callback(null, 'two');
}
],
function(err, results){
console.log(results);
});
Eredmény:
['one', 'two']
parallel(tasks, [callback])
async.parallel([
function(callback){
setTimeout(function(){
callback(null, 'one');
}, 200);
},
function(callback){
setTimeout(function(){
callback(null, 'two');
}, 100);
}
],
function(err, results){
console.log(results);
});
Eredmény:
['one', 'two']
waterfall(tasks, [callback])
async.waterfall([
function(callback) {
callback(null, 'one', 'two');
},
function(arg1, arg2, callback) {
//arg1: one, arg2: two
callback(null, 'three');
},
function(arg1,callback) {
//arg1: three
callback(null, 'done','done2');
}
],
function(err, result, result2){
console.log(result,result2);
}
Eredmény:
done done2
És még sokan mások...
Tessék dokumentációt olvasni hozzá: https://github.com/caolan/async
Instrumentáláshoz az egyik legjobb library.
Sok helyen láttuk már a mintázatot:
function callback(error,result){
...
}
A legtöbb esetben a node ezt használja hibajelzésre async esetben.
Példa:
fs.readFile('/etc/passwd', function (err, data) {
if (err) throw err;
console.log(data);
});
Ez egy jó mintázat! Ha async akarunk hibát jelezni, tegyük így! A throw esetében kaotikusabb megmondani, hol köt ki a hibánk.
Könnyű mockolni/tesztelni is.
Mindig nézzük meg, hogy az általunk használt modul / package / library / whatever mit használ.
Sok a promise és az error first callbackes megoldás is.