【问题标题】:ES6 Generators: poor stack trace from iterator.throw(err)ES6 生成器:来自 iterator.throw(err) 的不良堆栈跟踪
【发布时间】:2015-07-29 05:05:00
【问题描述】:

ES6 方法:iterator.throw(err) 通常被描述为注入异常,就好像它发生在生成器中的 yield 语句中一样。问题是此异常的堆栈跟踪不包含对 yield 语句的文件/行的任何引用,甚至不包含它所在的函数。相反,堆栈跟踪似乎仅在异常对象为 constructed,它不在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,如果它被拒绝,则使用此机制将错误转换为在 yield 语句处的 throw。

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


我正在使用 Chrome v42

【问题讨论】:

  • 如果你使用的是 bluebird,那么 Promise.coroutine 可以为你处理很多这样的事情。
  • 如果您认为缺少某些内容,您应该为 Chrome 提交错误报告/缺少功能报告。其他任何人都无能为力。我希望在所有 ES6 的所有浏览器中都可能出现更多这样的错误。 dev.chromium.org/for-testers/bug-reporting-guidelines
  • JavaScript 中的堆栈跟踪始终绑定到您创建的 Error 实例,并将始终显示在创建它们的行中。因此,您的堆栈跟踪将始终显示调用 Error 的行。
  • @BenjaminGruenbaum 虽然这是真的,但似乎给生成器的非平凡使用留下了真正的开发问题。当调用一个抛出的普通函数时,堆栈至少显示调用行。但是如果生成器等待失败的承诺,似乎没有办法找出“调用”代码堆栈。如果这不是实现错误,那么它似乎是一个设计问题。
  • 嗯,像 Bluebird 这样的库手动缝合堆栈跟踪。

标签: javascript ajax ecmascript-6 generator yield


【解决方案1】:

迭代器和 Promise 不能很好地混合(目前)——你实际上是在产生一个 Promise,然后在循环之外失败。

您可以通过将 Promise 的结果传递回生成器来解决这个问题,例如:

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);
}

仅:这是 asyncawait 无论如何都会做的事情(这些关键字只是用于生成返回结果的 Promise 的语法糖),如果您使用它们,则常规的 try-catch 将起作用。

iterator.throw 主要是一种停止迭代的方法,而不是向其中注入异常 - 堆栈顶部仍然是您创建 Error 的任何位置。

最后,即将在 Chrome 中推出的是 async iterators - 它们非常强大,而且都是关于 Promise 的迭代。

【讨论】:

    【解决方案2】:

    这种方法怎么样:

    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 ... Promise.all 版本是:

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

    我还建议查看其中一些工具:iter-tools

    【讨论】:

      猜你喜欢
      • 2023-03-05
      • 1970-01-01
      • 1970-01-01
      • 2014-11-24
      • 2012-11-08
      • 1970-01-01
      • 1970-01-01
      • 2017-02-21
      • 1970-01-01
      相关资源
      最近更新 更多