NodeJS Express async / await - PullRequest
       10

NodeJS Express async / await

0 голосов
/ 27 февраля 2019

По-прежнему овладеваю неблокирующим характером Node.Следующий код выполняется как задумано.Однако мне интересно, есть ли лучший подход для выполнения этой задачи.

На маршрут подается 3 параметра (почтовый индекс, тип, рад).Оттуда я использую пакет Zipcode NPM, чтобы возвратить массив почтовых индексов в пределах предоставленного rad.

Затем я использую цикл for в массиве zips внутри асинхронной функции и жду ответа от функции, которая выполняет запрос MySQL и возвращает обещание.Затем возвращаем массив пользовательских объектов.

Я не уверен, правильно ли я отправляю ответ или есть более эффективный способ написания этого кода.

Спасибо.

router.get('/:zipcode/:type/:rad', function (req, res) {

  const rad = req.params.rad;
  const zip = req.params.zipcode;
  let zips = zipcodes.radius(zip, rad);
  zips.push(zip);

  let type;
  if(req.params.type === 'bartenders') {
    type = 0;
  } else {
    type = 1;
  }

  const params = {
    'type': type,
    'zips': zips
  };


  function userGroup(type, zip) {
    return new Promise(resolve => {
      connection.query(`SELECT * FROM bt9.users WHERE zip = ${zip} AND type = ${type} AND display = 1`, function (err, result) {
        if(err) throw err;
        resolve(result);
      });
    });
  }


  async function getUsers(params) {
    let userList = [];
    for (i = 0; i < params.zips.length; i++) {
      const users = await userGroup(params.type, params.zips[i]);
      for (u = 0; u < users.length; u++) {
        userList.push(users[u]);
      }
    }
    return userList;
  }


  function sendUsers(callback) {
    getUsers(params).then( res => {
      callback(null, res)
    })
  }


  sendUsers(function(err, result) {
    if(err) throw err;
    res.send(result)
  })


});

Ответы [ 3 ]

0 голосов
/ 27 февраля 2019

Чтобы добавить к ответ Стивена Спангина , вот функция getUsers с рефакторингом Promise.all:

function getUsers({zips, type}) {
  return Promise.all(zips.map(zip => userGroup(type, zip)))
    .then(users => users.flat());
}

В качестве альтернативы, если вы не возражаете против использования стороннихмодуль, вы можете зайти в async-af и использовать его mapAF (псевдоним map) метод:

const aaf = require('async-af');
// ...
async function getUsers({zips, type}) {
  const userGroups = await aaf(zips).map(zip => userGroup(type, zip));
  return userGroups.flat(); // flat isn't yet part of 'async-af' but now that it's finalized for ES, I'm sure it'll be added soon.
}
0 голосов
/ 27 февраля 2019

Вместо того, чтобы вручную преобразовывать каждую функцию обратного вызова в Promise, проще создать оболочку для поддержки асинхронного / ожидающего.

Ссылка
https://strongloop.com/strongblog/async-error-handling-expressjs-es7-promises-generators/

function asyncWrapper(fn) {
  return (req, res, next) => {
    return Promise.resolve(fn(req))
      .then((result) => res.send(result))
      .catch((err) => next(err))
  }
}

Пример кода

async createUser(req) {
  const user = await User.save(req.body)
  return user
}

router.post('/users', asyncWrapper(createUser))

Рефакторинг вашего кода

function userGroup(type, zip) {
  return new Promise(resolve => {
    connection.query(`SELECT * FROM bt9.users WHERE zip = ${zip} AND type = ${type} AND display = 1`, function (err, result) {
      if(err) throw err;
      resolve(result);
    });
  });
}

async function getUsers({ type, zips }) {
  let userList = [];
  // [IMPORTANT]
  // - Replaced the for-loop to for-of-loop for await to work.
  // - This is not efficient because the `userGroup` function is run one by one serially.
  for (let zip of zips) {
    const users = await userGroup(type, zip);
    userList = userList.concat(users);
  }
  return userList;
}

router.get('/:zipcode/:type/:rad', asyncWrapper(async (req) => {
  const rad = req.params.rad;
  const zip = req.params.zipcode;
  let zips = zipcodes.radius(zip, rad);
  zips.push(zip);

  let type;
  if(req.params.type === 'bartenders') {
    type = 0;
  } else {
    type = 1;
  }

  return await getUsers({ type, zips });
}));

Чтобы еще больше повысить эффективность, вы должны заменить внутри петли внутри getUsers на Promise.map, предлагаемый bluebird .Promise.map будет выполнять обещания параллельно.

async function getUsers({ type, zips }) {
  let userList = []
  const userListList = await Promise.map(zips, (zip) => {
    return userGroup(type, zip);
  });
  for (let users of userListList) {
    userList = userList.concat(users)
  }
  return userList;
}
0 голосов
/ 27 февраля 2019

Не следует выдавать ошибку, если вы не находитесь внутри асинхронной функции.

function userGroup(type, zip) {
    return new Promise( (resolve,reject) => {
      connection.query(`SELECT * FROM bt9.users WHERE zip = ${zip} AND type = ${type} AND display = 1`, function (err, result) {
        if(err) return reject(err); //<- reject and return
        resolve(result);
      });
    });
  }

Также вы можете использовать Promise.all с массивом обещаний вместо await внутри каждой итерации цикла.Это позволит параллельное выполнение вашего соединения.

...