问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

用await-to-js让Promise操作更简洁

创作时间:
作者:
@小白创作中心

用await-to-js让Promise操作更简洁

引用
1
来源
1.
https://codlin.me/blog-program/await-to-js-keeps-your-promises-slick

前阵子意外注意到一个有趣的库,名为await-to-js。这个库的主要用途是提供一种新的Promise与await的写法,可以在特定情境下让代码更简洁。类似的实现Radash也有,不过await-to-js的用法比较单纯。

具体差异在哪里?

假设原本的Promise是这样:

let isLoading = true
task()
  .then(() => {
    isLoading = false
  })
  .catch((e) => {
    console.error('error')
    isLoading = false
  })

用await会这样写:

let isLoading = true
try {
  await task()
} catch (e) {
  console.error('error')
}
isLoading = false

使用await-to-js改写后变成:

import to from 'await-to-js'
let isLoading = true
const [error] = await to(task())
isLoading = false
if (error) {
  console.error('error')
  return
}

除了看起来比较简洁,不用try catch外,配合early return很有效。在前端可能还好,在后端各种Promise操作的场合的很方便,从此以后写法再也回不去了。

以下是官方的范例:

import to from 'await-to-js'
async function asyncTaskWithCb(cb) {
  let err, user, savedTask, notification;
  [err, user] = await to(UserModel.findById(1))
  if (!user)
    return cb('No user found');
  [err, savedTask] = await to(TaskModel({ userId: user.id, name: 'Demo Task' }))
  if (err)
    return cb('Error occurred while saving task')
  if (user.notificationsEnabled) {
    [err] = await to(NotificationService.sendNotification(user.id, 'Task Created'))
    if (err)
      return cb('Error while sending notification')
  }
  if (savedTask.assignedUser.id !== user.id) {
    [err, notification] = await to(NotificationService.sendNotification(savedTask.assignedUser.id, 'Task was created for you'))
    if (err)
      return cb('Error while sending notification')
  }
  cb(null, savedTask)
}
async function asyncFunctionWithThrow() {
  const [err, user] = await to(UserModel.findById(1))
  if (!user)
    throw new Error('User not found')
}

我自己的话后端比较常用,例如NestJS的Controller中:

@Controller()
export class ArticleController {
  // ..
  @UseGuards(AuthGuard('jwt'))
  async getInfo(
    @User() jwtPayload: JwtPayload,
    @Body() body: CreateArticleDto,
  ) {
    const [userError, result] = await to(
      this.userService.check(jwtPayload)
    )
    if (userError) {
      throw new HttpException(
        `用户身份异常`,
        HttpStatus.FORBIDDEN,
      )
    }
    const [checkError, result] = await to(
      this.articleService.checkQuota(jwtPayload)
    )
    if (checkError) {
      throw new HttpException(
        `建立文章失败,请稍后再试`,
        HttpStatus.INTERNAL_SERVER_ERROR,
      )
    }
    if (result.reason === 'quota-has-been-used-up') {
      throw new HttpException(
        `文章额度已满,请联系客服`,
        HttpStatus.BAD_REQUEST,
      )
    }
    const [createError, article] = await to(
      this.articleService.create(body)
    )
    if (createError) {
      throw new HttpException(
        `建立文章失败,请稍后再试`,
        HttpStatus.INTERNAL_SERVER_ERROR,
      )
    }
    return article
  }
}

可以提前处理error,更方便使用early return。

await-to-js的实现很简单,他的原始码只有22行:

/**
 * @param { Promise } promise
 * @param {object=} errorExt - Additional Information you can pass to the err object
 * @return { Promise }
 */
export function to<T, U = Error>(
  promise: Promise<T>,
  errorExt?: object
): Promise<[U, undefined] | [null, T]> {
  return promise
    .then<[null, T]>((data: T) => [null, data])
    .catch<[U, undefined]>((err: U) => {
      if (errorExt) {
        const parsedError = Object.assign({}, err, errorExt)
        return [parsedError, undefined]
      }
      return [err, undefined]
    })
}
export default to

如果不想npm install的话,也可以直接贴到自己的专案使用。

Promise都改用await to吗?

当然不是这样啦,await to只是多一个选择。实际上还是有些地方更适合使用then或try catch。

例如:

  • 有时候根本就不会有错误,就维持await。
  • 有错误也无所谓,不需要特别处理,用catch记录一下就行。
  • 单一错误不重要,统一处理即可,就直接try catch。

请依照实际状况挑选适合的写法。

总结

  • await-to-js可以在特定情境下简化Promise写法
  • 配合early return很有效
  • 请不要只用await-to-js,需要用then或try catch还是用原本的写法
© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号