首页 文章

为什么新Promise的reject()在可怕的“Uncaught(in promise)”错误中丢失堆栈跟踪

提问于
浏览
4

在Angular的github问题中搜索“Uncaught(in promise)”,谷歌或者这里的stackoverflow会产生很多非常具体的结果,但我的问题有点宽泛(而且由于结果的扩散,我不知道这是否是重复的)

前提:在我的代码的某个地方,我有一个未被批准的承诺拒绝(Angular 4.4.4)

案例:

Case 1: Simple Rejection

在MyComponent中:

ngOnInit() {
    Promise.reject("foo");
}

在控制台中产生这个(很好,我可以看到它来自哪里):

ERROR Error: Uncaught (in promise): foo
    at resolvePromise (zone.js:824)
    at Function.ZoneAwarePromise.reject (zone.js:903)
    at MyComponent.webpackJsonp.../../../../../src/app/component/my.component.ts.MyComponent.ngOnInit (my.component.ts:nn)

Case 2: new Promise, throwing an error

在MyComponent中:

ngOnInit() {
  new Promise((resolve, reject) => {
    throw new Error("foo");
  }).then(() => {
    console.log("ain't gonna happen");
  });
}

在控制台中产生这个(更好的是,当场放弃位置):

ERROR Error: Uncaught (in promise): Error: foo
Error: foo
    at my.component.ts:nn
    at new ZoneAwarePromise (zone.js:890)
    at MyComponent.webpackJsonp.../../../../../src/app/component/my.component.ts.LMyComponent.ngOnInit (my.component.ts:nn)

这也适用于链接,例如以下内容在控制台中或多或少地提供相同的堆栈跟踪 .

ngOnInit() {


  const promise3 = new Promise((resolve, reject) => {
    throw new Error("baz");
  });
  const promise2 = new Promise((resolve, reject) => {
    resolve(promise3);
  });

  const promise1 = new Promise((resolve, reject) => {
    resolve(promise2);
  });

  promise1.then(p1=> console.log(p1));
}

Case 3: Using reject(...)

在MyComponent中:

ngOnInit() {
  new Promise((resolve, reject) => {
    reject("foo");
  }).then(() => {
    console.log("ain't gonna happen");
  });
}

改变使用promise的拒绝方法抛出一个错误(我假设很常见的做法),产生了这个恐怖:

core.es5.js:1020 ERROR Error: Uncaught (in promise): baz
    at resolvePromise (zone.js:824)
    at resolvePromise (zone.js:795)
    at zone.js:873
    at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:425)
    at Object.onInvokeTask (core.es5.js:3881)
    at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:424)
    at Zone.webpackJsonp.../../../../zone.js/dist/zone.js.Zone.runTask (zone.js:192)
    at drainMicroTaskQueue (zone.js:602)
    at <anonymous>

Question

  • 为什么我这次不能看到堆栈跟踪或错误的来源? (我假设这只是因为JavaScript只是从抛出错误的位置生成堆栈跟踪?)

  • 如果是这样,使用显式拒绝而不是在新Promise中抛出新错误是不好的做法吗?是否记录在某处,我错过了?

  • 有什么方法可以解决这个问题吗? (获取堆栈跟踪并继续使用拒绝(...)而不是抛出错误?,我假设没有)

  • 这是一个众所周知的问题/不良做法,我只是错过了备忘录?

1 回答

  • 0

    部分回答我自己的一个问题:

    如果我将案例#3改为:(包装在一个新的Error对象中)

    ngOnInit() {
      new Promise((resolve, reject) => {
        reject(new Error("foo"));
      }).then(() => {
        console.log("ain't gonna happen");
      });
    }
    

    然后,出乎意料的是,我确实得到了堆栈跟踪! (仅构造错误保存堆栈跟踪)

    在旁注...从那以后我提到了最佳做法?:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

    Edit

    为了确保我不忘记,我添加了这个缩小( reject: (reason: Error) => void 而不是 any ),不确定这是否是一个很好的解决方案,因为它不能保护传递 any ,但它可以帮助保护一些不通过的情况一个 Error 拒绝 .

    export {}
    
    declare global {
    
      interface PromiseConstructor {
        /**
         * A reference to the prototype.
         */
        readonly prototype: Promise<any>;
    
        /**
         * Creates a new Promise.
         * @param executor A callback used to initialize the promise. This callback is passed two arguments:
         * a resolve callback used resolve the promise with a value or the result of another promise,
         * and a reject callback used to reject the promise with a provided reason or error.
         */
        new <T>(executor: (resolve: (value?: T | PromiseLike<T>) => void, reject: (reason: Error) => void) => void): Promise<T>;
    
      }
    }
    

相关问题