## Szerver oldali JavaScript **5. hét** Promise, async-await, error first callback -- PAGE -- ### Előadás témája Pár izgalmasabb javascript / node specifikus dolog. A nyelvi elemek előadásra épít erősen... -- PAGE -- ### Functionök / callbackek ```js module.exports = function (objectrepository) { return function (req, res, next) { userModel.findOne({}, function (err,results){ res.locals.users = results; return next(); }); return next(); }; }; ``` A **function** nagyon gyakran használt nyelvi elem. -- PAGE -- ### Egyszerű példa - fájl beolvasás ```js 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. -- PAGE -- ### Egyszerű példa - fájl beolvasás Használjunk promiset! ```js 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); }); ``` -- PAGE -- ### Promise 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!! ```js 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);}); ``` -- PAGE -- ### Promise 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) -- PAGE -- ### Promise 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. ```js function promiseFv(item){ return new Promise(function (resolve, reject) { resolve(item+'p'); });} ``` -- PAGE -- ### Promise lánc ```js 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** -- PAGE -- ### Promise lánc  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ó! -- PAGE -- ### Promise.all Több promise egy várakozásban összefogva - vagy megvárja mindegyik sikerét, vagy az első kudarcot. ```js 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"] }); ``` -- PAGE -- ### Promise.race Több promise, amelyik gyorsabban befejeződik, az adódik át a thenben (a többi is fut / lefut, nem szakítódik meg). ```js 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 }); ``` -- PAGE -- ### Promise.resolve, Promise.reject Szinkron function / érték becsomagolása promiseba, van ennek értelme. :) ```js function foo(){ return Promise.resolve('alma'); } function bar(){ return Promise.reject('alma'); } foo.then(bar).catch(console.log); ``` -- PAGE -- ### async - await ```js async function foo(){ //... try { await bar(); } catch(e){ // bar reject handle } } ``` async kontextus promiset ad vissza, await kulcsszó használható -- PAGE -- ### async - await ```js async function foo (){ //... } foo().then(e=>console.log(e)).catch(err=>console.log(err)); ``` Tényleg egy promise! -- PAGE -- ### fájl beolvasás ```js 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'); } } }); ``` -- PAGE -- ### Promise változat ```js 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]); }); ``` -- PAGE -- ### async - await változat ```js 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]); } ``` -- PAGE -- ### Promise Bizonyos esetekben ez kell, bizonyos esetekben normál callbackek, illetve beszéljünk az **async npm modulról**. ``` npm i async ``` -- PAGE -- ### Async [Async.js](https://github.com/caolan/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. -- PAGE -- ### Async - each, eachSeries, eachLimit - each(arr, iterator, [callback]) - eachSeries(arr, iterator, [callback]) - eachLimit(arr, limit, iterator, [callback]) ```js 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: ```js Processing:1 Processing:2 Processing:3 Processing done:3 Processing done:1 Processing done:2 All done ``` -- PAGE -- ### Async - map (mapSeries, mapLimit) map(arr, iterator, [callback]) ```js const lista = [1,2,3]; async.map(lista, function (item, callback) { callback(null, item * item); }, function (err, results) { console.log(results); }); ``` Eredmény: ```js [ 1, 4, 9 ] ``` -- PAGE -- ### Async - series series(tasks, [callback]) ```js async.series([ function(callback){ callback(null, 'one'); }, function(callback){ callback(null, 'two'); } ], function(err, results){ console.log(results); }); ``` Eredmény: ```js ['one', 'two'] ``` -- PAGE -- ### Async - parallel parallel(tasks, [callback]) ```js 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: ```js ['one', 'two'] ``` -- PAGE -- ### Async - waterfall waterfall(tasks, [callback]) ```js 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: ```js done done2 ``` -- PAGE -- ### Async És még sokan mások... Tessék dokumentációt olvasni hozzá: [https://github.com/caolan/async](https://github.com/caolan/async) Instrumentáláshoz az egyik legjobb library. #### Minden feladatfüggő: - async - promise (promisify) - callback - sima függvények - aszinkron fv -- PAGE -- ### Error first callback Sok helyen láttuk már a mintázatot: ```js function callback(error,result){ ... } ``` A legtöbb esetben a node ezt használja hibajelzésre async esetben. -- PAGE -- ### Error first callback Példa: ```js 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. -- PAGE -- ### Error first callback 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.