Estoy usando ExpressJS para API y uso muchas promesas para solicitudes asincrónicas como llamadas a API externas, consultas de bases de datos. En la mayoría de los casos está bien, pero cuando tengo muchas llamadas, el código se vuelve complicado debido a muchas declaraciones. Then -> .catch. Aquí hay un ejemplo de una API de inicio de sesión que realiza llamadas a la API, consultas de base de datos

const loginUser = (req, res) => {
  const { body } = req;

  console.log('Login');

  if (!body) {
    return res
      .status(400)
      .json({ success: false, error: 'request body is empty' });
  }

  if (!body.authCode) {
    return res
      .status(400)
      .json({ success: false, error: 'authCode missing in the body' });
  }

  getLinkedInProfile(body.authCode)
    .then((linkedInProfile) => {
      User.findOne({ linkedInId: linkedInProfile.linkedInId })
        .then((user) => {
          if (user) {
            Token.findOne({ userId: user._id })
              .then((token) => {
                // if token isn't in our DB, store
                if (!token) {
                  // encrypt information
                  const t = util.refreshToken(user._id);

                  Token.create({ refreshToken: t, userId: user._id });
                }
                // send the access token
                const accessToken = util.accessToken(user._id);

                return (
                  res
                    // .cookie('accessToken', accessToken, {
                    //   sameSite: 'none',
                    //   secure: true,
                    // })
                    .json({
                      success: true,
                      message: 'Login Successful',
                      token: accessToken,
                      user: {
                        _id: user._id,
                        userType: user.userType,
                      },
                    })
                );
              })
              .catch((err) => {
                console.log(err);
                return res.status(500).json({
                  success: false,
                  error: 'Error in find token',
                });
              });
          } else {
            return res.status(200).json({
              success: false,
              message: 'User does not exist',
            });
          }
        })
        .catch((err) => {
          console.log(err);
          return res.status(500).json({
            success: false,
            error: 'Unable to query database',
          });
        });
    })
    .catch((err) => {
      console.log(err);
      return res.status(500).json({
        success: false,
        error: 'Unable to authenticate linkedIn profile',
      });
    });
};

¿Alguien puede ayudar a revisar esto / sugerir formas de hacer que el código sea más limpio?

1
user3655266 14 mar. 2021 a las 01:02

2 respuestas

La mejor respuesta

Las promesas requieren un poco de trabajo adicional si desea un error personalizado en cada paso del camino. Normalmente trato de hacer varias cosas para simplificar y eliminar la repetición:

  1. Envíe todos los errores desde un solo lugar, en lugar de a través del código.
  2. Deje que los errores se propaguen hasta el nivel superior try/catch o catch.
  3. Cree propiedades personalizadas en el objeto Error para realizar un seguimiento de lo que desea que sea el resultado final del error para que pueda lanzar ese Error y luego interceptarlo más tarde y saber cuál desea que sea la respuesta de error en un lugar central.
  4. Cree una función auxiliar para construir este error personalizado.
  5. Registre cualquier excepción o rechazo real para que siempre podamos ver cuál fue el error original, especialmente si lo estamos cambiando a nuestro propio mensaje personalizado. Esto es muy importante al depurar en el servidor donde desea ver cuál es el error real de bajo nivel.

Luego, debido a que desea un error personalizado en cada paso del camino, solo se necesita una gran cantidad de detección de errores personalizados. Aunque estoy usando await en algunos lugares, uso .catch() para la producción de errores personalizados porque simplifica enormemente el código. A algunas personas no les gusta ver await y .catch() usados ​​juntos, pero si escribes este código sin .catch(), verás inmediatamente por qué esto es mucho más simple, por lo que esta es una excepción. permitirme.

Y, por último, usar async y await aplana el código y hace que el flujo sea mucho más simple. También facilita que todos los errores fluyan al nivel superior catch si los arrojamos.

Aquí está el código:

// utility function to construct an error object, 
// set some properties and then throw the error object
function throwError(status, msg, e) {
    if (e) {
        // log the actual error before we change it to something else
        console.log(e);
    }
    const err = new Error(msg);
    err.json = { success: false, error: msg };
    err.status = status;
    throw err;
}

const loginUser = async (req, res) => {
    try {
        const { body } = req;

        console.log('Login');

        if (!body) {
            throwError(400, 'request body is empty');
        }

        if (!body.authCode) {
            throwError(400, 'authCode missing in the body');
        }

        const linkedInProfile = await getLinkedInProfile(body.authCode)
            .catch(err => throwError(500, 'Unable to query database', err));

        const user = await User.findOne({ linkedInId: linkedInProfile.linkedInId })
            .catch(err => throwError(500, 'Unable to authenticate linkedIn profile', err));

        if (!user) {
            throwError(200, 'User does not exist');
        }
        const token = await Token.findOne({ userId: user._id })
            .catch(err => throwError(500, 'Error in find token', err));

        // if token isn't in our DB, store
        if (!token) {
            // encrypt information
            const t = util.refreshToken(user._id);
            Token.create({ refreshToken: t, userId: user._id });
        }
        // send the access token
        const accessToken = util.accessToken(user._id);
        res.json({
            success: true,
            message: 'Login Successful',
            token: accessToken,
            user: {
                _id: user._id,
                userType: user.userType,
            },
        });

    } catch (e) {
        // send error response here from all prior errors
        let status = e.status | 500;
        let json = e.json | { success: false, error: e.message };
        res.status(status).json(json);
    }
}
1
jfriend00 14 mar. 2021 a las 00:56

Simplemente use async / await en un try / catch

const loginUser = async (req, res) => {
const { body } = req;

console.log('Login');

if (!body) {
  return res
    .status(400)
    .json({ success: false, error: 'request body is empty' });
}

if (!body.authCode) {
  return res
    .status(400)
    .json({ success: false, error: 'authCode missing in the body' });
}
try{
const linkedInProfile = await getLinkedInProfile(body.authCode)
.
.
.
}catch(err){
  console.log(err);
  return res.status(500).json({
    success: false,
    error: 'Unable to authenticate linkedIn profile',
  });

}

Async / await: https://javascript.info/async-await

1
JsThiago 14 mar. 2021 a las 02:23