天天看點

在express中使用async middleware

普通middleware

const express = require('express')
const app = express()
const port = 3000

app.use('/normal', (req, res) => res.send('Hello World!'))

app.listen(port, () => console.log(`Example app listening on port ${port}!`))           

文藝middleware (async)

const express = require('express')
const app = express()
const port = 3000

app.use('/art', async (req, res) => {
  await new Promise((resolve, reject) => {
    setTimeout(resolve, 1000)
  })
  res.send('Bonjour le monde!')
})

app.listen(port, () => console.log(`Example app listening on port ${port}!`))           

萬一middleware裡報錯了怎麼辦

增加一個4參數的中間件作為error handler,當然express也有一個預設的handler
const express = require('express')
const app = express()
const port = 3000

app.use('/normal', (req, res) => res.send('Hello World!'))
app.use('/normal-error', (req, res, next) => {
  throw new Error('some error')
})

app.use((err, req, res, next) => res.send('error detected: ' +  err.message))

app.listen(port, () => console.log(`Example app listening on port ${port}!`))           

async middleware裡的報錯也這麼處理?

答案是No
const express = require('express')
const app = express()
const port = 3000

app.use('/art', async (req, res) => {
  await new Promise((resolve, reject) => {
    setTimeout(resolve, 1000)
  })
  res.send('Bonjour le monde!')
})
app.use('/2b-error-1', async (req, res) => {
  await new Promise((resolve, reject) => {
    setTimeout(() => reject('another error 1'), 1000)
  })
})
app.use('/2b-error-2', async (req, res) => {
  await Promise.resolve().then(() => {
    throw new Error('another error 2')
  })
})

app.use((err, req, res, next) => res.send('error detected: ' +  err.message))

app.listen(port, () => console.log(`Example app listening on port ${port}!`))           

如果隻這樣寫的話,浏覽器裡不會有任何傳回,而你會在終端裡得到下面的報錯:

在express中使用async middleware
在express中使用async middleware

正确處理async middleware裡的報錯

const express = require('express')
const app = express()
const port = 3000

app.use('/art', async (req, res) => {
  await new Promise((resolve, reject) => {
    setTimeout(resolve, 1000)
  })
  res.send('Bonjour le monde!')
})
app.use('/art-error-1', async (req, res, next) => {
  try {
    await new Promise((resolve, reject) => {
      setTimeout(() => reject(new Error('another error 1')), 1000)
    })
  } catch (e) {
    next(e)
  }
})
app.use('/art-error-2', async (req, res, next) => {
  await Promise.resolve().then(() => {
    throw new Error('another error 2')
  }).catch(next)
})

app.use((err, req, res, next) => res.send('error detected: ' +  err.message))

app.listen(port, () => console.log(`Example app listening on port ${port}!`))           

處理的辦法就是catch住異步操作中可能會抛出的錯誤,這裡其實沒有新的内容,在express的

文檔

裡已經說了必須捕獲異步操作中的錯誤:

You must catch errors that occur in asynchronous code invoked by route handlers or middleware and pass them to Express for processing.

每個異步操作都如此try/catch,太麻煩了

解決辦法是寫一個高階函數

asyncMiddleware

,在這個函數裡面給傳進來的middleware自動加上catch方法。

const express = require('express')
const app = express()
const port = 3000

app.use('/art', async (req, res) => {
  await new Promise((resolve, reject) => {
    setTimeout(resolve, 1000)
  })
  res.send('Bonjour le monde!')
})

// 重點
function asyncMiddleware(fn) {
  return (req, res, next) => fn(req, res, next).catch(next)
}
app.use('/art-error-1', asyncMiddleware(async (req, res, next) => {
  await new Promise((resolve, reject) => {
    setTimeout(() => reject(new Error('another error 1')), 1000)
  })
}))
app.use('/art-error-2', asyncMiddleware(async (req, res, next) => {
  await Promise.resolve().then(() => {
    throw new Error('another error 2')
  })
}))

app.use((err, req, res, next) => res.send('error detected: ' +  err.message))

app.listen(port, () => console.log(`Example app listening on port ${port}!`))           
如果不确定參數是否一定為async function,可以使用Promise.resolve(fn(req, res, next))