избежать множественных возвратов, зацикленных в javascript - async / await для решения пирамиды обратного вызова или ада обратного вызова,

1

У меня есть этот код с большим количеством блоков возврата, например, 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 этот тип кода возврата блока? вызывающие функции, которые снова возвращают код?

ОБНОВЛЕНО:

Код не мой, полный источник в

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 функции вызова возврата и возвращающие значения и функции? ООП никогда не бывает так, как использовать подсказку Async/Await от @dashmud, я не хочу изучать старые вещи, такие как обратные вызовы

  • 1
    «вернуть функции , которые возвращают функции» - Когда вы говорите , return reject(...) , что вызывает reject() функция немедленно , а затем возвращается независимо от его возвращаемого значения. Это довольно нормально почти во всех современных языках, и это не то же самое, что возвращение функции.
  • 2
    Добро пожаловать в функциональное программирование - что-то ужасное в PHP.
Показать ещё 3 комментария
Теги:
ecmascript-6
express
ecmascript-2017

3 ответа

3
Лучший ответ

Расширение @jfriend00 ответа, вот такой подход, который использует ES2017 async/await синтаксис для выравнивания обратного вызова пирамиды или обратного вызова в ад, но вы предпочитаете называть его:

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

Вместо того, чтобы переписывать мою собственную обещанную на основе обещания encryptPassword(), я решил использовать встроенную функцию преобразования node.js для функции util.promisify().

В целом, есть еще несколько улучшений, которые можно было бы сделать, например, перенос проверки аргументов signUp() в отдельную функцию, но ни одна из них не связана с выравниванием анти-шаблона "обратного ада", что привело к появлению на основе обещаний управляющий поток и синтаксис async/wait.

  • 0
    Ты не веришь в точки с запятой?
  • 0
    @ Jfriend00 Я недавно (в начале этого года) принял этот стиль, который я считаю приятным: standardjs.com . Я понимаю, что это не официально одобренный стандарт или что-то в этом роде, просто я предпочитаю писать сейчас.
Показать ещё 5 комментариев
4

Этот код необходимо реорганизовать несколькими способами. Во-первых, вы действительно не хотите смешивать обещания и простые асинхронные обратные вызовы в одном логическом потоке. Это создает беспорядок и разрушает множество преимуществ обещаний. Затем у вас есть анти-шаблон, использующий обещания внутри new Promise(). Затем у вас больше гнездования, которое требуется (вы можете цепью вместо этого).

Вот что я предлагаю:

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) все ошибки и возвращайте Promise.reject(err) в одном месте для всех этих исходных ошибок.
  2. Promisify encryptPassword() чтобы вы не смешивали регулярные обратные вызовы с логикой логики обещаний, и тогда вы можете правильно возвращать и распространять ошибки.
  3. Удаление обертывания всего кода с return new Promise() поскольку все ваши операции async теперь обещают, поэтому вы можете просто сразу отправить обещания. Это также помогает при правильной обработке ошибок.
  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 - это управление потоком программы (выход из содержащейся функции), но поскольку после этого возврата нет кода на том же уровне, это лишний.
  3. Вы передаете обратный вызов encryptPassword(). Этот обратный вызов является асинхронным, то есть он называется через некоторое время.
  4. Обратный вызов получает два аргумента: err и hash. В соглашении вызова async node.js, если первый аргумент является правдивым (например, содержит некоторое правдоподобное значение), тогда он представляет ошибку. Если он является ложным (обычно null), тогда нет ошибки, а второй аргумент содержит результат асинхронной операции.
  5. Если произошла ошибка, родительское обещание отклоняется и обратный вызов завершается. Опять же, нет никакого возвращаемого значения из reject поэтому return - это только выход из обратного вызова в этой точке (так что другой код в return вызове не работает).
  6. Затем User.create() другая асинхронная операция User.create() и передается объект args с установленным в ней новым паролем.
  7. User.create() возвращает обещание, поэтому его результат захватывается с помощью метода .then() и обратного вызова, переданного этому.
  8. Спустя некоторое время, когда User.create() асинхронный User.create(), он решит свое обещание, и это вызовет обратный вызов .then() вызова, и результат будет передан ему. Если в этой операции произошла ошибка, тогда не будет вызван обратный вызов .then(), вместо этого будет .catch() обратный вызов .catch(). В этом .catch() родительское обещание отклоняется. Это обещание anti-pattern (разрешение родительского обещания внутри другого обещания), потому что очень легко ошибиться в правильной обработке ошибок. Мой ответ показывает, как этого избежать.
  • 0
    Не возражаете, если я расширю ваш код, чтобы дать ответ ES7, или вы уже работаете над этим?
  • 0
    @PatrickRoberts - я был бы рад, если бы вы расширили версию ES7.
Показать ещё 10 комментариев
-2

Прежде всего. В коде нет петель.

Если вы придете с PHP, я бы предположил, что асинхронная модель исполнения Javascript выглядит вам чуждым. Я бы посоветовал узнать больше о Javascript.

Узнайте следующее в следующем порядке:

  1. Callbacks
  2. обещания
  3. Генераторы
  4. Асинхронный /Await

Это разные подходы к управлению асинхронной моделью Javascript, начиная с самых простых (обратных вызовов) до самого современного подхода, "async/await".

Async/await будет наиболее читаемым, но было бы лучше, если бы вы поняли, как появился async/await, поскольку они просты в использовании, если вы не понимаете, что делаете.

  • 1
    Генераторы раньше обещаний? Интересное предложение.
  • 0
    Хотя я согласен с тем, что генераторы являются важной продвинутой темой в JavaScript, я не очень понимаю, как они вписываются в рассматриваемый вопрос. Не могли бы вы уточнить, почему вы упоминаете их?
Показать ещё 4 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню