“用 Promise.then 容易出现回调地狱,所以改用 async/await” 这个说法对吗

昨天周会同事做技术分享,讲到一点:“用 Promise.then 容易出现回调地狱,所以改用 async/await”。那么这个说法对吗?

首先这个说法完全错误。“Promise.then 出现回调地狱”,完全是编码不规范的问题,而不是 api 的问题。来看下面这个例子:

// 解析压缩包
function parseZip(zip) {
  return new Promise(
    resolve => setTimeout(resolve, 1000, zip)
  );
}

// 从压缩包中获取 yaml 文件
function getYaml(unpack) {
  return new Promise(
    resolve => setTimeout(resolve, 1000, unpack)
  );
}

// 解析 yaml 文件为数组
function parseYaml(yaml) {
  return new Promise(
    resolve => setTimeout(resolve, 1000, yaml)
  );
}

这边我们需要按顺序先后调用三个函数,即将第一个函数返回的结果传入第二个函数,将第二个函数返回的结果传入第三个函数,以此类推。如果编码不规范,就会出现下面这个情况:

parseZip("zip files")
  .then(res => {
    getYaml(res)
      .then(res => {
        parseYaml(res)
          .then(res => {
            console.log("最终结果:", res);
          })
      })
  })

这样写问题很大,一方面出现回调地狱,另一方面异常捕获变得困难

那么按规范的写法是怎么样的呢?我们知道 then 方法是有返回值的,并且返回的是一个 Promsie。如果 then 方法内部的回调函数返回非 Promise 类型,那么 then 方法就会把这个值包裹在 Promise 中返回;如果 then 方法内部的回调函数本身返回一个 Promise,那么 then 方法直接把这个 Promise 透传出去。我们可以充分利用这一点来优化逻辑:

parseZip("zip files")
  .then(res => getYaml(res))
  .then(res => parseYaml(res))
  .then(res => console.log(res))
  .catch(err => logReport(err));

可以看到改为链式调用就变得优雅了,异常捕获也很简单,其中任意一步执行出错,都会中断整个 Promise 链,然后被最后的 catch 方法捕获到

其实还可以再进一步优化,我们知道 then 方法的入参就是一个回调函数,上面的代码中我们在外面包裹了一层函数用来透传参数,但实际上直接传递函数表达式即可:

parseZip("zip files")
  .then(getYaml)
  .then(parseYaml)
  .then(console.log.bind(console))
  .catch(logReport);

其次,这两句话前后根本没有因果关系。Promise.then 链式调用和 async/await 同步的写法,完全是两种编程风格而已。用 async/await 写着很舒服,但是异常捕获会变得比较麻烦:

async function execute() {
  try {
    const unpack = await parseZip("zip files");
    const yaml = await getYaml(unpack);
    const res = await parseYaml(yaml);
    console.log(res);
  } catch (error) {
    console.log(error);
  }
}

或者改成这样:

async function execute() {
  const unpack = await parseZip("zip files");
  const yaml = await getYaml(unpack);
  const res = await parseYaml(yaml);
  return res;
}

execute()
  .then(res => console.log(res))
  .catch(err => console.log(err));

可以看到其实并没有简单多少,本人在平时开发中主要还是在用 Promise.then,这样更方便一些:

this.loading = true;
parseZip("zip files")
  .then(getYaml)
  .then(parseYaml)
  .then(console.log.bind(console))
  .catch(err => {
    // 捕获整个 Promise 链的异常
    console.log(err);
  })
  .finally(() => {
    // 无论成功或失败都要进行的操作
    this.loading = false;
  })
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值