evitar múltiples devoluciones en bucle en javascript – async / await para resolver la pirámide de callback o el infierno de callback,

Tengo este código, con muchos bloques de devoluciones, por ejemplo SignUp ()

conectores.js

const connectors = { Auth: { signUp(args) { return new Promise((resolve, reject) => { // Validate the data if (!args.email) { return reject({ code: 'email.empty', message: 'Email is empty.' }); } else if (!isEmail(args.email)) { return reject({ code: 'email.invalid', message: 'You have to provide a valid email.' }); } if (!args.password) { return reject({ code: 'password.empty', message: 'You have to provide a password.' }); } return encryptPassword(args.password, (err, hash) => { if (err) { return reject(new Error('The password could not be 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 is already a user with this email.' }); } return reject(err2); }); }); }); }, }; module.exports = connectors; 

luego un código de anotador que llama a este código:

  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); }); } 

¿Cómo es posible evitar esto en ES6 o ES7 o ES2017?

existen:

  return() .then() return() .then 

y solo devuelve bucle:

 return() return() return() 

procedente de PHP, este código parece una locura, porque las funciones de devolución devuelven funciones, y no está claro para mí, ¿cómo es el nombre en JavaScript de este tipo de código de retorno de bloque? ¿Llamando a las funciones que devuelve más código?

ACTUALIZADO:

El código no es mío, fuente completa en

https://github.com/jferrettiboke/react-auth-app-example

Me gustaría entender por ejemplo:

  return encryptPassword(args.password, (err, hash) => { if (err) { return reject(new Error('The password could not be hashed.')); } return User.create(Object.assign(args, { password: hash })) .then((user) => { .catch((err2) => { return reject(err2); 

/src/utils/auth.js (aquí está encryptPassword)

 const jwt = require('jsonwebtoken'); const bcrypt = require('bcrypt-nodejs'); const config = require('../config'); exports.encryptPassword = (password, callback) => { // Generate a salt then run callback bcrypt.genSalt(10, (err, salt) => { if (err) { return callback(err); } // Hash (encrypt) our password using the salt return bcrypt.hash(password, salt, null, (err2, hash) => { if (err2) { return callback(err2); } return callback(null, hash); }); }); }; 

¿Hay 3 devoluciones llamando a funciones y devolviendo valores y funciones? OOP nunca es así, cómo usar Async / Await Suggest por @dashmud, no quiero aprender cosas antiguas como devoluciones de llamada

Extendiendo la respuesta de @ jfriend00 , aquí hay un enfoque que utiliza la syntax async / await ES2017 para aplanar la pirámide de callback o el infierno de callback , como prefiera llamarlo:

 const encryptPasswordPromise = require('util').promisify(encryptPassword) const connectors = { Auth: { async signUp (args) { const { email, password } = args // Validate the data let err if (!email) { err = { code: 'email.empty', message: 'Email is empty.' } } else if (!isEmail(email)) { err = { code: 'email.invalid', message: 'You have to provide a valid email.' } } else if (!password) { err = { code: 'password.empty', message: 'You have to provide a password.' } } if (err) { throw err } let hash try { hash = await encryptPasswordPromise(password) } catch (err) { throw new Error('The password could not be hashed.') } const { _id: id, email } = await User.create(Object.assign(args, { password: hash })) try { return createToken({ id, email }) } catch (err) { if (err.code === 11000) { throw { code: 'user.exists', message: 'There is already a user with this email.' } } else { throw err } } } } } module.exports = connectors 

En lugar de escribir a mano mi propia promesa cifrada basada en la encryptPassword() , opté por usar una función de transformación incorporada node.js para la llamada util.promisify() .

En general, todavía se pueden realizar algunas mejoras, como mover la validación de los argumentos signUp() a una función separada, pero nada de eso está relacionado con el aplanamiento del antipatrón “infierno de callback” que dio lugar a una promesa basada en la promesa. Sintaxis control-flow y async / await.

Este código debe ser refactorizado de varias maneras. Primero, realmente, realmente no desea mezclar promesas y devoluciones de llamadas asíncronas en el mismo flujo lógico. Hace un desastre y destruye muchas de las ventajas de las promesas. Entonces, tienes un anti-patrón utilizando promesas dentro de la new Promise() . Luego, tiene más anidamiento que se requiere (en su lugar, puede encadenar).

Esto es lo que sugeriría:

 function encryptPasswordPromise(pwd) { return new Promise((resolve, reject) => { encryptPassword(pwd, (err, hash) => { err ? reject(new Error("The password could not be hashed.")) : resolve(hash); }); }); } const connectors = { Auth: { signUp(args) { // Validate the data let err; if (!args.email) { err = {code: 'email.empty', message: 'Email is empty.'}; } else if (!isEmail(args.email)) { err = {code: 'email.invalid', message: 'You have to provide a valid email.'}; } else if (!args.password) { err = {code: 'password.empty', message: 'You have to provide a 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 is already a user with this email.'}); } else { throw err2; } }); } } } }; 

Resumen de Cambios:

  1. Recopile todos los errores inicialmente y devuelva Promise.reject(err) en un lugar para todos esos errores iniciales.
  2. Promise a encryptPassword() para que no esté mezclando devoluciones de llamadas regulares con el flujo lógico de promesa y, a continuación, puede devolver y propagar errores correctamente.
  3. Elimine la envoltura de todo el código con la return new Promise() ya que todas las operaciones asíncronas están ahora prometidas, por lo que puede devolver las promesas directamente. Esto realmente ayuda con el manejo adecuado de errores también.
  4. Deshacer el anidamiento innecesario y simplemente encadenar.
  5. Elimine Object.assign() ya que no parece haber una razón para ello.

En su edición, solicitó una explicación de este segmento de código:

 return encryptPassword(args.password, (err, hash) => { if (err) { return reject(new Error('The password could not be hashed.')); } return User.create(Object.assign(args, { password: hash })) .then((user) => { .catch((err2) => { return reject(err2); 
  1. Este código llama a encryptPassword() .
  2. Devuelve el resultado de lo que probablemente undefined está undefined por lo que todo lo que está haciendo el return es controlar el flujo del progtwig (salir de la función que lo contiene), pero como no hay código después de ese retorno al mismo nivel, eso no es necesario.
  3. encryptPassword() una callback a encryptPassword() . Esa callback es asíncrona, lo que significa que se llama algún tiempo después.
  4. La callback obtiene dos argumentos: err y hash . En la convención de llamada asíncrona node.js, si el primer argumento es veraz (por ejemplo, contiene algún valor de verdad), entonces representa un error. Si es falsey (normalmente null ), entonces no hay ningún error y el segundo argumento contiene el resultado de la operación asíncrona.
  5. Si hubo un error, la promesa principal se rechaza y se cancela la callback. Nuevamente, no hay un valor de retorno del reject por lo que la return es solo para salir de la callback en ese punto (por lo que no se ejecuta ningún otro código en la callback).
  6. Luego, se llama a otra operación asíncrona User.create() y se pasa el objeto args con una nueva contraseña establecida en él.
  7. User.create() devuelve una promesa por lo que su resultado se captura con un método .then() y se le .then() una callback.
  8. Algún tiempo después, cuando el User.create() asíncrono.create User.create() finalice, resolverá su promesa y eso hará que se .then() callback .then() y se le pasará el resultado. Si hay un error en esa operación, entonces no se llamará la callback .catch() , en su lugar se .catch() callback .catch() . En esa callback .catch() , la promesa principal es rechazada. Esta es una promesa anti-patrón (resolver una promesa de los padres dentro de otra promesa) porque es muy fácil cometer errores en el manejo adecuado de los errores. Mi respuesta muestra cómo evitar eso.

Lo primero es lo primero. No hay bucles en su código.

Si viene de PHP, supongo que el modelo de ejecución asíncrona de Javascript le parece ajeno. Sugeriría aprender más sobre Javascript.

Aprende lo siguiente en el siguiente orden:

  1. Devoluciones de llamada
  2. Promesas
  3. Generadores
  4. Async / Await

Esos son los diferentes enfoques sobre cómo manejar el modelo de ejecución asíncrono de Javascript desde el más básico (devoluciones de llamada) hasta el enfoque más moderno, “async / await”.

Async / await sería el más legible, pero sería mejor si comprendiera cómo async / await llegó a ser, ya que son fáciles de usar de forma incorrecta si no entiende lo que está haciendo.

    Intereting Posts