async 的语法糖 函数是如何捕获异常的?——大佬间文章矛盾

我的神
  • 966

async 函数的实现,就是将 Generator 函数和自动执行器,包装在一个函数里。

async function fn(args){
  // ...
}

// 等同于

function fn(args){ 
  return spawn(function*() {
    // ...
  }); 
}

function spawn(genF) {
  return new Promise(function(resolve, reject) {
    var gen = genF();
    function step(nextF) {
      try {
        var next = nextF();
      } catch(e) {
        return reject(e); 
      }
      if(next.done) {
        return resolve(next.value);
      } 
      Promise.resolve(next.value).then(function(v) {
        step(function() { return gen.next(v); });      
      }, function(e) {
        step(function() { return gen.throw(e); });
      });
    }
    step(function() { return gen.next(undefined); });
  });
}

摘自 http://www.ruanyifeng.com/blog/2015/05/async.html

没想明白 spawn 里的catch 什么情况下才会执行了?
和 async await 一样的效果吗?

下面是 async await 捕获异常的写法。
摘自:https://segmentfault.com/a/1190000019854513

async function run() {
    // 这里return 和 await 都可以
    return Promise.reject(new Error("Oops!"));
}
run()
    .catch(function handleError(err) {
        err.message; // Oops!
    })
    .catch(err => {
        process.nextTick(() => {
            throw err;
        });
    });

矛盾来了:

为什么我把 run 写成 上面 generator的形式。没有同样的效果?

function run(args){ 
  return spawn(function*() {
     return Promise.reject(new Error("Oops!"));
  }); 
}
run()
    .catch(function handleError(err) {
        err.message; // Oops!
    })
回复
阅读 1.3k
2 个回答
✓ 已被采纳
  1. 首先我们知道你传递给 new Promise 的函数是会立即执行的,也就是说你传递给 spawn 的 generator 会立即实例化得到 gen ,也就是第三行 var gen = genF();
  2. 接下来是一个 step 函数声明,先越过去,看到底下立即调用了 step 还给了一个匿名函数当参数。
  3. step 内部上来就是一个 try-catch 包住的 nextFn 调用,来到了题主关注的地方。那么这个 try-catch 捕获的是什么呢?是 nextFn 内部的异常。
  4. nextFn 实际是什么?纵观整个 spawn 函数,nextFn 可能的实参内部调用的东西就两种,gen.next gen.throw。也就是说,try-catch 捕获的就是这两个操作中可能抛出的异常。
  5. gen.next gen.throw 是什么?这你就需要复习一下 Generator 的知识了。
  6. 简单讲,Generator 就是一个可以执行到中途不继续执行,外部戳一下再继续走的函数。简单的类比就是报数,比如每报一个数停一次,外面的人叫你报下一个就是 gen.next,你用 yield value 就是往外报数,外面的人能拿到你报的数 next.value,也能判断出你有没有结束 next.done。如果觉得你报的数有问题,就用 next.throw 告诉你这个报的数有问题你要处理下。
  7. 因此 spawn 中 catch 到的错误是 genF中每次从报上一个数(或最开始)到报下一个数中可能抛出的异常,即两次 yield 之间执行的代码;以及如果出错了,错误交给你后你在处理错误的过程中可能犯的二次错误。
  8. 这个和原生的 async-await 是保持了行为一致的。

@will 那下面 g.throw ('出错了')后, 第二个catch 为什么捕获不到 ?

function* gen(x){
  try {
    var y = yield x + 2;
  } catch (e){ 
    console.log(1,e);
  }
  return y;
}

var g = gen(1);
g.next();

try{
   g.throw ('出错了'); 
} catch (e){ 
  console.log(2,e);
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
宣传栏