簡體   English   中英

NodeJS Express 異步/等待

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM