mzi*_*ert 5 javascript node.js express async-await
仍然掌握了 Node 的非阻塞性质。以下代码按预期执行。但是,我想知道是否有更好的方法来完成任务。
有 3 个参数提供给路由(邮政编码、类型、rad)。从那里我使用 NPM Zipcode 包在提供的 rad 内返回一组邮政编码。
然后我在异步函数中的 zips 数组上使用 for 循环,并等待执行 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)
})
});Run Code Online (Sandbox Code Playgroud)
Express 5 将自动正确处理异步错误
https://expressjs.com/en/guide/error-handling.html目前说得很清楚:
从 Express 5 开始,返回 Promise 的路由处理程序和中间件将在拒绝或抛出错误时自动调用 next(value)。例如:
Run Code Online (Sandbox Code Playgroud)app.get('/user/:id', async function (req, res, next) { var user = await getUserById(req.params.id) res.send(user) })如果 getUserById 抛出错误或拒绝,则将使用抛出的错误或拒绝的值调用 next 。如果未提供拒绝值,则将使用 Express 路由器提供的默认错误对象来调用 next。
我已经在实验中证明了这一点:将异步函数传递给 Node.js Express.js 路由器
这意味着您将能够直接进行回调async并使用await它,而无需任何额外的包装器:
router.get('/:zipcode/:type/:rad', async (req) => {
...
return await getUsers({ type, zips });
});
Run Code Online (Sandbox Code Playgroud)
请注意,截至 2021 年 12 月,Express 5 仍处于 alpha 版本,不建议用于生产使用。
与手动将每个回调函数转换为 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))
}
}Run Code Online (Sandbox Code Playgroud)
示例代码
async createUser(req) {
const user = await User.save(req.body)
return user
}
router.post('/users', asyncWrapper(createUser))Run Code Online (Sandbox Code Playgroud)
重构你的代码
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 });
}));Run Code Online (Sandbox Code Playgroud)
为了进一步提高效率,就应该更换换的环内getUsers与Promise.map所提供的蓝鸟。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;
}Run Code Online (Sandbox Code Playgroud)
当您不在异步函数内时,不应抛出错误。
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);
});
});
}
Run Code Online (Sandbox Code Playgroud)
此外,您可以使用Promise.allPromise 数组,而不是await在每个循环迭代中使用。这将允许并行执行您的连接。