NodeJS Express异步/等待

时间:2019-02-27 02:30:45

标签: javascript node.js express async-await

仍然可以理解Node的非阻塞性质。以下代码按预期执行。但是,我想知道是否有更好的方法来完成任务。

有3个参数提供给该路线(邮政编码,类型,弧度)。 从那里,我使用NPM Zipcode包在提供的rad中返回一个邮政编码数组。

然后,我在异步函数内的zips数组上使用for循环,并等待执行MySQL查询并返回promise的函数的响应。然后返回一个用户对象数组。

我的不确定性是我是否正确发送了响应,或者是否有更有效的方式编写此代码。

谢谢。

import { Component, OnInit } from "@angular/core";
import { StationService } from "../services/station.service";
import { Station } from "./../../models/station.model";

@Component({
  selector: "app-station",
  templateUrl: "./station.component.html",
  styleUrls: ["./station.component.css"]
})
export class StationComponent implements OnInit {
  stations: Station[];

  constructor(private stationService: StationService) {}

  ngOnInit() {
    this.stationService.getStations().subscribe(data => {
      this.stations = data.map(e => {
        return {
          id: e.payload.doc.data(),
          ...e.payload.doc.data()
        } as Station;
      });
    });
  }

  delete(id: string) {
    if (confirm("Confirm delete operation")) {
      //console.log(id);
      this.stationService.deleteStation(id);
    }
  }
}

4 个答案:

答案 0 :(得分:1)

当您不在异步函数中时,请勿抛出错误。

<<Mowing1>>

此外,您可以将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数组一起使用,而不是在每次循环迭代中使用Promise.all。这将允许并行执行连接。

答案 1 :(得分:0)

要添加到Steven Spungin's answer,以下是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.
}

答案 2 :(得分:0)

与其将每个回调函数手动转换为Promise,还不如创建一个支持async / await的包装器。

  

参考
  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 });
}));

要进一步提高效率,您应该用bluebird提供的getUsers替换Promise.map中的for-for循环。 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;
}

答案 3 :(得分:0)

Express 5 自动正确处理异步错误

https://expressjs.com/en/guide/error-handling.html 目前明确表示:

<块引用>

从 Express 5 开始,返回 Promise 的路由处理程序和中间件将在拒绝或抛出错误时自动调用 next(value)。例如:

app.get('/user/:id', async function (req, res, next) {
 var user = await getUserById(req.params.id)
 res.send(user)
})

如果 getUserById 抛出错误或拒绝,next 将使用抛出的错误或拒绝的值调用。如果没有提供拒绝值,next 将使用 Express 路由器提供的默认 Error 对象调用。

我在实验中证明了这一点:Passing in Async functions to Node.js Express.js router

这意味着您将能够直接调用 async 并从中使用 await,而无需任何额外的包装器:

router.get('/:zipcode/:type/:rad', async (req) => {
  ...
  return await getUsers({ type, zips });
});