仍然可以理解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);
}
}
}
答案 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 });
});