避免在javascript中循环多次返回-async/await以解决回调金字塔或回调地狱,

避免在javascript中循环多次返回-async/await以解决回调金字塔或回调地狱,,javascript,node.js,express,ecmascript-6,ecmascript-2017,Javascript,Node.js,Express,Ecmascript 6,Ecmascript 2017,我有这段代码,有很多返回块,例如SignUp() 连接器.js const connectors = { Auth: { signUp(args) { return new Promise((resolve, reject) => { // Validate the data if (!args.email) { return reject({

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

连接器.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中的名称如何表示这种类型的块返回代码?调用再次返回更多代码的函数

更新:

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

我想通过例子来理解:

    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(这里是加密密码)


调用函数和返回值和函数有3个返回?OOP从来不是这样的,如何使用@dashmud建议的Async/Await,我不想先学习回调之类的老东西。代码中没有循环

如果您来自PHP,那么我猜Javascript的异步执行模型对您来说是陌生的。我建议学习更多关于Javascript的知识

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

  • 回调
  • 许诺
  • 发电机
  • 异步/等待
  • 这些是处理Javascript异步执行模型的不同方法,从最基本的(回调)到最现代的方法“async/await”


    Async/await是最具可读性的,但如果您了解Async/await是如何产生的,那就更好了,因为如果您不了解自己在做什么,它们很容易使用错误的方式。

    首先要做的事情。代码中没有循环

    如果您来自PHP,那么我猜Javascript的异步执行模型对您来说是陌生的。我建议学习更多关于Javascript的知识

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

  • 回调
  • 许诺
  • 发电机
  • 异步/等待
  • 这些是处理Javascript异步执行模型的不同方法,从最基本的(回调)到最现代的方法“async/await”


    Async/await是最具可读性的,但如果您了解Async/await是如何产生的,那就更好了,因为如果您不了解自己在做什么,它们很容易以错误的方式使用。

    此代码需要以多种方式重构。首先,您真的,真的不想在同一逻辑流中混合承诺和普通异步回调。它把事情搞得一团糟,破坏了承诺的许多好处。然后,在
    newpromise()
    中有一个反模式正在使用承诺。然后,您需要更多的嵌套(您可以改为链)

    以下是我的建议:

    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;
                        }
                    });
                }
            }
        }
    };
    
    更改摘要:

  • 最初收集所有错误,并针对所有这些初始错误在一个位置返回
    Promise.reject(err)
  • Promisify
    encryptPassword()
    ,这样您就不会将常规回调与promise逻辑流混为一谈,然后可以正确地返回和传播错误
  • 使用
    return new Promise()
    删除对整个代码的包装,因为您的所有异步操作现在都已完成,所以您可以直接返回承诺。这也确实有助于正确的错误处理
  • 撤消不必要的嵌套,只需链即可
  • 删除
    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);
    
  • 此代码调用
    encryptPassword()
  • 它返回的结果可能是
    未定义的
    ,因此
    返回
    所做的一切都是控制程序流(退出包含函数),但由于在相同级别的返回之后没有代码,因此这是不必要的
  • 您将回调传递给
    encryptPassword()
    。该回调是异步的,这意味着它将在一段时间后调用
  • 回调函数得到两个参数:
    err
    hash
    。在node.js异步调用约定中,如果第一个参数是truthy(例如,包含一些truthy值),则表示错误。如果为false(通常为
    null
    ),则没有错误,第二个参数包含异步操作的结果
  • 如果出现错误,则拒绝父承诺并退出回调。同样,
    reject
    没有返回值,因此
    return
    仅用于在该点退出回调(因此回调运行中没有其他代码)
  • 然后,调用另一个异步操作
    User.create()
  • User.create()
  • 一段时间后,当异步
    User.create()
    完成时,它将解析其承诺,这将导致调用
    .then()
    回调,并将结果传递给它。如果该操作出错,则不会调用
    .then()
    回调,而是调用
    .catch()
    回调。在该
    .catch()
    回调中,父承诺被拒绝。这是一种承诺反模式(在另一个承诺中解析父承诺),因为在正确的错误处理中很容易出错。我的回答说明了如何避免这种情况

  • 这段代码需要以多种方式重构。首先,您真的,真的不想在同一逻辑流中混合承诺和普通异步回调。它把事情搞得一团糟,破坏了承诺的许多好处。然后,在
    newpromise()
    中有一个反模式正在使用承诺。然后,你有更多的巢
    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);
    
    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