aysnc函数错误处理

213 阅读2分钟

最近,搭配koa和mysql2 (sequelize)使用的时候,查询数据库,会要捕获错误,sequelize提供的模型操作方法,均是返回promise对象,我们可以用then... catch 这样的链式方法 来获取查询数据结果,捕获错误;但是koa 支持 aysnc/ await 语法,我们可以更简洁地操作。

	// 查询用户信息
user = await Models.users.findOne({
  where:{
    username: userName,
    password:mdPwd
  }
})

// 创建用户信息
rs = await Models.users.build({
  user_id:userId,
  username:userName,
  password:mdPwd
}).save()

try ... catch

​ 在使用aync/await,我们可以像写同步代码一样来完成数据的增删该查,但是这种方式我们无法捕获数据操作过程中的错误信息。如果需要捕获错误,我们可以借助 try {} catch{}。 下面是我demo中的错误处理代码

// 查询用户信息
let {userName,userPwd} = ctx.request.body;
	const mdPwd = md5(userPwd)
  // console.log(userName,userPwd)
  let user
  try{
    user = await Models.users.findOne({
      where:{
        username: userName,
        password:mdPwd
      }
    })
  }catch(err){
      // 捕获错误
    return ctx.body = {
      status: "1",
      msg: err.message
    }
  }

  // 用户名和密码已存在
    if(user){
    return ctx.body = {
      status: '22',
      msg: '用户名和密码已存在'
    }
  }
  let userId = (Math.floor(Math.random() * 100000) + 100000000);
  let rs;
  // 写入用户信息,错误捕获
  try{
    rs = await Models.users.build({
      user_id:userId,
      username:userName,
      password:mdPwd
    }).save()
  }catch(error){
    return ctx.body = {
      status: '1',
      msg: err1.message,
      result: ''
    }
  }

​ 上述 try {} catch{} 捕获错误的方法,也是繁琐,因为aysnc/await 处理的函数均会返回一个promise对象,

我们可以包装promise,使其返回统一的格式的代码

 /**
   * 包装promise, 使其返回统一的错误格式
   * @param {Promise} promise 
   */
  function handlerAsyncError (promise) {
    return promise.then(res => [null, res]).catch(err => [err])
  }
 
// 简洁处理错误,减少try。。catch
  const [err, res] = await handlerAsyncError(fetchUser(true))
  if (err) {
    console.error('touser err:', err)
  }

async 顺序

​ 使用async的时候,代码执行的顺序很容易出错,比如我们要同时发起两个请求,可能会写出下面的代码

function fetchName () {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('lujs')
    }, 3000)
  })
}

function fetchAvatar () {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('https://avatars3.githubusercontent.com/u/16317354?s=88&v=4')
    }, 4000)
  })
}

async fetchUser () {
  const name = await fetchName()
  const avatar = await fetchAvatar()
  return {
    name,
    avatar
  }
}

​ 在上面的代码中,我们认为fetchName,fetchAvatar会并行执行,实际上并不会。fetchAvatar会等待fetchName执行完之后才开始请求。要并行请求的话需要像下面这样写:

async function fetchUserParallel () {
  const namePromise = fetchName()
  const avatarPromise = fetchAvatar()
  return {
    name: await namePromise,
    avatar: await avatarPromise 
  }
}

使用Promise.all来并发请求

function fetchList (id) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(`id is : ${id}`)
    }, 5000)
  })
}

async function getList () {
  const ary = [1, 2, 3, 4]
  const list =  Promise.all(
    ary.map(
      (id) => fetchList(id)))
  return await list
}