node.js - avoid multiple returns looped in javascript - async / await to solve callback pyramid or callback hell, -
i have code, lot of blocks of returns, example signup()
connectors.js
const connectors = { auth: { signup(args) { return new promise((resolve, reject) => { // validate data if (!args.email) { return reject({ code: 'email.empty', message: 'email empty.' }); } else if (!isemail(args.email)) { return reject({ code: 'email.invalid', message: 'you have provide valid email.' }); } if (!args.password) { return reject({ code: 'password.empty', message: 'you have provide password.' }); } return encryptpassword(args.password, (err, hash) => { if (err) { return reject(new error('the password not hashed.')); } return user.create(object.assign(args, { password: hash })) .then((user) => { resolve(createtoken({ id: user._id, email: user.email })); }) .catch((err2) => { if (err2.code === 11000) { return reject({ code: 'user.exists', message: 'there user email.' }); } return reject(err2); }); }); }); }, }; module.exports = connectors;
then anoter code call code:
const connectors = require('./connectors'); callsignup(root, args) { const errors = []; return connectors.auth.signup(args) .then(token => ({ token, errors })) .catch((err) => { if (err.code && err.message) { errors.push({ key: err.code, value: err.message }); return { token: null, errors }; } throw new error(err); }); }
how it's possible avoid in es6 or es7 or es2017?
there are:
return() .then() return() .then
and loop returns:
return() return() return()
comming php code looks crazy, because return functions return functions, , it's not clear me, how name in javascript type of block return code? calling functions returns again more code?
updated:
code not mine, full source in
https://github.com/jferrettiboke/react-auth-app-example
i'd understand example:
return encryptpassword(args.password, (err, hash) => { if (err) { return reject(new error('the password not hashed.')); } return user.create(object.assign(args, { password: hash })) .then((user) => { .catch((err2) => { return reject(err2);
/src/utils/auth.js (here encryptpassword)
const jwt = require('jsonwebtoken'); const bcrypt = require('bcrypt-nodejs'); const config = require('../config'); exports.encryptpassword = (password, callback) => { // generate salt run callback bcrypt.gensalt(10, (err, salt) => { if (err) { return callback(err); } // hash (encrypt) our password using salt return bcrypt.hash(password, salt, null, (err2, hash) => { if (err2) { return callback(err2); } return callback(null, hash); }); }); };
there 3 returns calling functions , returning values , functions? oop never this, how use async/await suggest @dashmud, don't want learn old things callbacks
this code needs refactored in number of ways. first, really, don't want mix promises , plain async callbacks in same logic flow. makes mess , wrecks lot of advantages of promises. then, have anti-pattern going using promises inside of new promise()
. then, have more nesting required (you can chain instead).
here's i'd suggest:
function encryptpasswordpromise(pwd) { return new promise((resolve, reject) => { encryptpassword(pwd, (err, hash) => { err ? reject(new error("the password not hashed.")) : resolve(hash); }); }); } const connectors = { auth: { signup(args) { // validate data let err; if (!args.email) { err = {code: 'email.empty', message: 'email empty.'}; } else if (!isemail(args.email)) { err = {code: 'email.invalid', message: 'you have provide valid email.'}; } else if (!args.password) { err = {code: 'password.empty', message: 'you have provide password.'}; } if (err) { return promise.reject(err); } else { return encryptpasswordpromise(args.password).then(hash => { args.password = hash; return user.create(args); }).then((user) => { return createtoken({id: user._id, email: user.email}); }).catch(err2 => { if (err2.code === 11000) { throw new error({code: 'user.exists', message: 'there user email.'}); } else { throw err2; } }); } } } };
summary of changes:
- collect errors , return
promise.reject(err)
in 1 place initial errors. - promisify
encryptpassword()
aren't mixing regular callbacks promise logic flow , can return , propagate errors. - removing wrapping of whole code
return new promise()
since async operations promisified can return promises directly. helps proper error handling too. - undo unneccessary nesting , chain instead.
- remove
object.assign()
there did not appear reason it.
in edit, asked explanation of code segment:
return encryptpassword(args.password, (err, hash) => { if (err) { return reject(new error('the password not hashed.')); } return user.create(object.assign(args, { password: hash })) .then((user) => { .catch((err2) => { return reject(err2);
- this code calls
encryptpassword()
. - it returns result of
undefined
return
doing controlling program flow (exiting containing function), since there no code after return @ same level, that's unnecessary. - you pass callback
encryptpassword()
. callback asynchronous, meaning called time later. - the callback gets 2 arguments:
err
,hash
. in node.js async calling convention, if first argument truthy (e.g. contains truthy value), represents error. if falsey (usuallynull
), there no error , second argument contains result of asynchronous operation. - if there error, parent promise rejected , callback exited. again, there no return value
reject
return
exiting callback @ point (so no other code in callback runs). - then, asynchronous operation
user.create()
called , passed args object new password set in it. user.create()
returns promise result captured.then()
method , callback passed that.- some time later, when asynchronous
user.create()
finishes, resolve promise , cause.then()
callback called , result passed it. if there's error in operation,.then()
callback not called, instead.catch()
callback called. in.catch()
callback, parent promise rejected. promise anti-pattern (resolving parent promise inside promise) because easy make mistakes in proper error handling. answer shows how avoid that.
Comments
Post a Comment