[英]NodeJS 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) }) });
與手動將每個回調函數轉換為 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 }); }));
為了進一步提高效率,您應該將getUsers
的 for-of-loop 替換為bluebird提供的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; }
當您不在異步函數中時,不應拋出錯誤。
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
。 這將允許並行執行您的連接。
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。 如果未提供拒絕值,則將使用 Express 路由器提供的默認錯誤對象調用 next。
我已經在實驗中證明了這一點:將異步函數傳遞給 Node.js Express.js 路由器
這意味着您將能夠使回調async
並直接使用它的await
,而無需任何額外的包裝器:
router.get('/:zipcode/:type/:rad', async (req) => {
...
return await getUsers({ type, zips });
});
要補充Steven Spungin 的回答,這里是使用Promise.all
重構的getUsers
函數:
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.
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.