ES6生成器:从iterator.throw(err)中跟踪堆栈错误

ES6 Generators: poor stack trace from iterator.throw(err)

本文关键字:err 跟踪 错误 堆栈 throw iterator ES6      更新时间:2023-09-26

ES6方法:iterator.throw(err)通常被描述为注入异常,就好像它发生在生成器中的yield语句中一样。问题是,此异常的堆栈跟踪不包含对yield语句的文件/行甚至它所在的函数的任何引用。相反,堆栈跟踪似乎只在异常对象构造时生成,而异常对象不在generator中。

问题是:如何在堆栈跟踪或其他方式中获得违规yield语句的位置?

function* one_of_many_generators() {
    // ...
    yield ajax(url);    // <-- what I need in the stack trace
    // ...
}
function outer() {
    var iterator = one_of_many_generators();
    iterator.next();    // runs to the first yield
    // inject exception at the yield statement
    iterator.throw(Error("error"));   // <-- top of stack trace shows here
}

虽然这个问题不是Promises所特有的,但它们可能使问题更容易描述。在我的例子中,我使用的是带有生成器和承诺的任务系统。假设函数ajax()返回一个Promise,如果该Promise被拒绝,则使用此机制将错误转换为yield语句的throw。

调试器中的堆栈跟踪是非常无用的,因为我找不到一种方法来获得 yield statement 的函数、文件或行号,该注入发生在那里。调用iterator.throw(err)被视为重新抛出,并且不会获得新的堆栈信息,因此它只显示ajax()函数内部可以从许多地方调用的位置,并且通过在outer()中抛出一个新的错误,就像上面的例子一样,相同的抛出行显示所有错误。两者都没有给出关于调试错误时正在执行的generator函数的提示。


我使用Chrome v42

迭代器和承诺器还不能很好地混合在一起——你实际上是在生成一个承诺,然后在循环外失败。

你可以通过将承诺的结果传递回生成器来绕过这个问题,比如:

function* one_of_many_generators() {
    // ...
    var promiseResult = yield ajax(url);    // <-- what I need in the stack trace
    // Now we're back in the generator with the result of the promise
    if(notHappyWithResult(promiseResult))
        throw new Error('Reason result is bad');
    // ...
}
async function outer() {
    var iterator = one_of_many_generators();
    let prms = iterator.next();    // runs to the first yield
    // Wait for the promise to finish
    let result = await prms;
    // Pass the result back to the generator
    let whatever = iterator.next(result);
}

Only:这就是asyncawait所做的事情(这些关键字只是承诺生成器的语法糖,返回结果),如果你使用它们,一个常规的try-catch也可以工作。

iterator.throw主要是一种停止迭代的方式,而不是向它注入异常——堆栈的顶部仍然是你创建Error的地方。

最后,即将在Chrome中出现的是async迭代器-这些非常强大,都是关于承诺的迭代。

这个方法怎么样:

async function example() {
  const arrayOfFetchPromises = [
    fetch('1.txt'),
    fetch('2.txt'),
    fetch('3.txt')
  ];
  // Regular iterator:
  for (const item of arrayOfFetchPromises) {
    console.log(item); // Logs a promise
  }
  // Async iterator:
  for await (const item of arrayOfFetchPromises) {
    console.log(item); // Logs a response
  }
}

在这种情况下,for-awaitarray中获取每个项并等待其解析。即使第二个响应还没有准备好,你也会得到第一个响应,但你总是会以正确的顺序得到响应。您可以简单地处理拒绝,例如使用相同的旧.catch:)但是这种模式很容易导致未处理的拒绝。总有Promise.all…的承诺。所有版本为:

const [value1, value2] = await Promise.all([getValue1Async(), getValue2Async()]);

我还建议您查看以下一些工具: