避免在 javascript 中循环多个 returns - async / await 来解决回调金字塔或回调地狱,

avoid multiple returns looped in javascript - async / await to solve callback pyramid or callback hell,

我有这段代码,有很多 return 块,例如 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

然后循环 returns:

return()
   return()
       return()

来自 PHP 这段代码看起来很疯狂,因为 return 的功能是 return 的功能,我不清楚,javascript 中的名称如何块类型 return 代码?再次调用 return 的函数更多代码?

更新:

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

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 个 return 调用函数和 returning 值和函数? OOP 从来不是这样的,@dashmud 建议如何使用 Async/Await,我不想学习像回调这样的老东西

要事第一。您的代码中没有循环。

如果您来自 PHP,那么我猜想 Javascript 的异步执行模型对您来说看起来很陌生。我建议多了解 Javascript.

按以下顺序学习:

  1. 回调
  2. 承诺
  3. 发电机
  4. Async/Await

这些是关于如何处理 Javascript 的异步执行模型的不同方法,从最基本的(回调)到最现代的方法 "async/await"。

Async/await 将是最具可读性的,但如果您了解 async/await 是如何形成的,那会更好,因为如果您不理解您的内容,它们很容易以错误的方式使用正在做。

此代码需要以多种方式进行重构。首先,您真的、真的不想在同一个逻辑流中混合使用 promises 和普通的异步回调。它弄得一团糟,破坏了 promise 的许多优点。然后,你有一个在 new Promise() 中使用 promises 的反模式。然后,您需要更多嵌套(您可以改为链接)。

这是我的建议:

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. 最初收集所有错误,然后在一个地方对所有这些初始错误执行 return Promise.reject(err)
  2. Promisify encryptPassword() 这样您就不会将常规回调与 promise 逻辑流混合在一起,然后您可以正确地 return 并传播错误。
  3. 使用 return new Promise() 删除整个代码的包装,因为您的所有异步操作现在都已承诺,因此您可以直接 return 承诺。这也确实有助于正确处理错误。
  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. 它 return 可能是 undefined 的结果,所以 return 所做的只是控制程序流(退出包含函数),但由于没有return 之后的代码在同一级别,这是不必要的。
  3. 您将回调传递给 encryptPassword()。该回调是异步的,这意味着它会在一段时间后被调用。
  4. 回调有两个参数:errhash。在 node.js 异步调用约定中,如果第一个参数是真值(例如包含一些真值),那么它表示一个错误。如果为假(通常为 null),则没有错误,第二个参数包含异步操作的结果。
  5. 如果出现错误,父承诺将被拒绝并退出回调。同样,reject 中没有 return 值,因此 return 仅用于在该点退出回调(因此回调中没有其他代码运行)。
  6. 然后,另一个异步操作 User.create() 被调用,它被传递给其中设置了新密码的 args 对象。
  7. User.create() return 是一个承诺,所以它的结果是用 .then() 方法捕获的,并传递给它的回调。
  8. 一段时间后,当异步 User.create() 完成时,它将解决其承诺,这将导致调用 .then() 回调并将结果传递给它。如果该操作出错,则不会调用 .then() 回调,而是调用 .catch() 回调。在 .catch() 回调中,父承诺被拒绝。这是一个 promise 反模式(在另一个 promise 中解析父 promise),因为在正确的错误处理中很容易出错。我的回答说明了如何避免这种情况。

扩展 , here's an approach that uses the ES2017 async / await syntax to flatten the callback pyramid or callback hell,但您喜欢这样称呼它:

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

我没有手写我自己的基于 promise 的 promisified encryptPassword(),我选择使用 node.js 内置转换函数来调用 util.promisify()

总的来说,仍然可以进行一些改进,例如将 signUp() 参数的验证移动到一个单独的函数,但是 none 其中与扁平化“回调地狱”有关" 产生基于承诺的控制流和 async/await 语法的反模式。