避免在javascript中循环多个返回 – asynchronous/等待解决callback金字塔或callback地狱,

我有这个代码,有很多返回块,例如SignUp()

connectors.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; 

然后调用这个代码的代码:

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

在ES6或ES7或ES2017中如何避免这种情况?

有:

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

只是循环返回:

 return() return() return() 

来自PHP的这段代码看起来很疯狂,因为返回函数返回函数,而且对我来说还不清楚,javascript这个types的块的名字怎么返回代码呢? 调用函数,返回更多的代码?

更新:

代码不是我的,完整的源代码

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

我想通过例子来理解:

  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(这里是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); }); }); }; 

有3个返callback用函数和返回值和函数? 面向对象是从来没有这样,如何使用asynchronous/等待build议@dashmud,我不想学习像callback旧的东西

这个代码需要通过多种方式进行重构。 首先,你真的不想在相同的逻辑stream程中混合承诺和简单的asynchronouscallback。 它弄得一塌糊涂,破坏了许多承诺的优点。 然后,你在new Promise()有一个使用new Promise()的反模式。 然后,你需要更多的嵌套(你可以链接)。

以下是我的build议:

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

变更摘要:

  1. 最初收集所有错误,并在一个地方将Promise.reject(err)返回给所有这些初始错误。
  2. Promisify encryptPassword()所以你不是混合定期的callback与诺言逻辑stream程,然后你可以正确地返回和传播错误。
  3. 使用return new Promise()去除整个代码的包装,因为所有的asynchronous操作现在都被promisified,所以你可以直接返回promise。 这真的有助于正确的error handling。
  4. 撤消不必要的嵌套,而只是链。
  5. 删除Object.assign() ,因为似乎没有它的原因。

在您的编辑中,您要求解释这段代码:

 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. 此代码调用encryptPassword()
  2. 它返回可能undefined的结果,所以所有return操作是控制程序stream(退出包含函数),但是由于在同一级返回后没有代码,所以这是不必要的。
  3. 您将callback传递给encryptPassword() 。 这个callback是asynchronous的,这意味着它稍后会被调用。
  4. callback得到两个参数: errhash 。 在node.jsasynchronous调用约定中,如果第一个参数是truthy(例如包含一些真值),则表示错误。 如果它是falsey(通常为null ),那么没有错误,第二个参数包含asynchronous操作的结果。
  5. 如果发生错误,父承诺被拒绝,退出callback。 同样,从reject没有返回值,所以return只是在这一点退出callback(所以callback中没有其他代码运行)。
  6. 然后,调用另一个asynchronous操作User.create() ,并在其中设置一个新密码来传递args对象。
  7. User.create()返回一个承诺,以便使用User.create()方法捕获结果.then()并向其传递callback。
  8. 一段时间后,当asynchronous的User.create()完成时,它将解决它的承诺,这将导致调用.then()callback,并将结果传递给它。 如果在该操作中有错误,则不会调用.catch()callback,而是调用.catch()callback。 在.catch()callback中,父承诺被拒绝。 这是一种承诺反模式(在另一个承诺中解决父承诺),因为在正确的error handling中犯错是非常容易的。 我的答案显示如何避免这一点。

扩展@ jfriend00的答案 ,这里是一个使用ES2017 async / await语法来平整callback金字塔或callback地狱的方法 ,但是您更喜欢调用它:

 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 

我select使用一个名为util.promisify()的node.js内置转换函数,而不是手写我自己的Promisified promise-based util.promisify()

总的来说,还有一些改进可以做到,比如将signUp()参数的validation移动到一个单独的函数中,但是没有一个与平坦的“callback地狱”反模式有关,这导致了基于promise的控制stream和asynchronous/等待语法。

首先是事情。 你的代码中没有循环。

如果你来自PHP,那么我猜想Javascript的asynchronous执行模式对你来说看起来很陌生。 我会build议学习更多关于Javascript。

按以下顺序学习以下内容:

  1. callback
  2. 承诺
  3. 发电机
  4. asynchronous/等待

这些是如何处理Javascript的asynchronous执行模型的不同方法,从最基本的(callback)到最现代的方法,“asynchronous/等待”。

asynchronous/等待将是最可读的,但是如果你了解asynchronous/等待的方式会更好,因为如果你不明白你在做什么,它们很容易被错误的使用。