KieSun / all-of-frontend

你想知道的前端内容都在这

Home Page:https://yuchengkai.cn/docs/frontend

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

第一题:以下代码输出什么,为什么?

KieSun opened this issue · comments

commented
try {
    (async function() { a().b().c() })()
} catch (e) {
    console.log(`执行出错:${e.message}`)
}

第一反应答案是什么?为什么会这样报错?

答案

新建了一个大厂真题每日打卡群,有意愿学习打卡的再进,群已达扫码上线,请加好友拉你进群

Fucking Awesome !

- VM404:2 Uncaught (in promise) ReferenceError: a is not defined
  • a 函数未定义,a is not defined
  • try catch 无法捕获异步错误
  • async 是 Generator 生成器的语法糖,会报发生在 in Promise 错误

a is not defined

ReferenceError: a is not defined
a 都没有定义

ReferenceError: a is not defined
try catch 无法捕获异步错误

a is not defined

a is not defined

async 约等于 Promise

执行try中语句时报错ReferenceError: a is not defined,因此catch也捕获不到错误。

ReferenceError: a is not defined

ReferenceError: a is not defined
(async function() {a().b().c() })()是个异步任务,try...catch无法捕捉,所以不会执行catch中的console
改成

try {
    await (async function() { a().b().c() })()
} catch (e) {
    console.log(`执行出错:${e.message}`)
}

就可以catch啦执行出错:a is not defined

执行a()时,因为a未定义,所以会报错
但因为try里面async是异步,所以不会被同步的catch捕获,即不会有console输出
最终会返回一个rejected的Promise, 所以报的错为 Uncaught (in promise) ReferenceError: a is not defined

a is not defined

ReferenceError: a is not defined

async定义的是异步任务,catch无法捕获

如果有异步函数的话,就不需要用try catch 了

ReferenceError: a is not defined;
try执行,async方法出错,相当于promise reject,错误没被catch捕获

what‘s the fuck you doing here?

至于为啥catch 没有捕捉到,楼上很多的人都没有解释很清楚,我来回答一下我的陋见:
async + function 返回的是一个promise,promise 在自执行(FFEE)的一个作用域中是个完整的异步函数自执行了,内层函数已经执行完了,外层当然catch 不到。
想要被catch ,是在try 中发生了错误,代码中断。而一个完整promise只会在自身这一层被捕获,外面认为你这是自己的事,不能管啊。出来闹,我就管!

try catch 不能捕获异步代码。 所以也不是单纯的考量promise吧。虽然他也属于异步

1.try catch捕获不到该错误
2.按照正常语法来说 错误应该是 ReferenceError类型 a is not defined
3.可以修改一下 加一个await关键字 获取promise的返回值,使其能够被捕获到
4.也可以直接使用promise自带的catch捕获错误 (与题目无关了)

1.try里面是一个自执行的async函数,async是异步的方法,try catch没办法捕获到异步方法
2.async函数返回的是promise,因为内部发生了错误,所以变成promise rejected了,错误则是a方法未定义

个人见解: try catch是同步的,而async是异步的,返回的是一个Promise。而try catch只能捕获到同步函数中的异常,无法捕获到异步函数中的异常错误。而async中执行的,a().b().c(),一开始的a就会报错,因为a未定义。报错为:Uncaught ReferenceError: a is not defined
at :1:1

我又要来提灵魂之问了?为啥try catch 不能捕获异步任务的error ,这跟浏览器的执行机制(事件循环)有关:
try catch 作为宏任务而promise是微任务,当执行promise.then(()=>callbaclk()),进入回调函数的时候,外层try catch 这个宏任务已经出栈了,跟他已经没有关系了

  1. ReferenceError: a is not defined
  2. 执行异步方法a().b().c()的时候 try catch已经出栈执行完毕

给出2个例子 方便理解

// 异步,宏任务
try {
	setTimeout(function() {
		console.log(b);
	}, 0);
} catch (error) {
	console.log('error', error); // 这里是不会执行的
}
console.log('out try catch')

// 异步,微任务
try {
	new Promise(() => {
		throw new Error('new promise throw error');
	});
} catch (error) {
	console.log('error', error);
}

原因是因为:当异步函数抛出异常时,对于宏任务而言,执行函数时已经将该函数推入栈,此时并不在 try-catch 所在的栈,所以 try-catch 并不能捕获到错误。对于微任务而言,比如 promise,promise 的构造函数的异常只能被自带的 reject 也就是.catch 函数捕获到。

try {
    let a = 0
    ;(async function() {
        a += 1
        console.log('inner', a)
        throw new Error('123')
        // a()
    })()
    console.log('outer', a)
} catch(e) {
    console.warn('Error', e)
}

这段代码,inner 会在 outer 之前输出,为啥?

ReferenceError: a is not defined
try catch正常来说是可以捕获到错误的,但是由于try里面的是异步任务;try catch作为宏任务,在函数执行的时候就已经入栈,而当异步函数抛出异常时,try catch已出栈

try {
    let a = 0
    ;(async function() {
        a += 1
        console.log('inner', a)
        throw new Error('123')
        // a()
    })()
    console.log('outer', a)
} catch(e) {
    console.warn('Error', e)
}

这段代码,inner 会在 outer 之前输出,为啥?

promise是异步。 throw new Error('123')的时候错误被promise.reject接收,然后继续执行同步代码,等同步代码执行完执行异步爆出这个错误

try {
    let a = 0
    ;(async function() {
        a += 1
        console.log('inner', a)
        throw new Error('123')
        // a()
    })()
    console.log('outer', a)
} catch(e) {
    console.warn('Error', e)
}

这段代码,inner 会在 outer 之前输出,为啥?

本人补充一下,为啥要等 outer 输出完后,再抛错

commented

ReferenceError: a is not defined
(async function() {a().b().c() })()是个异步任务,try...catch无法捕捉,所以不会执行catch中的console
改成

try {
    await (async function() { a().b().c() })()
} catch (e) {
    console.log(`执行出错:${e.message}`)
}

就可以catch啦执行出错:a is not defined

老哥,你是第一位给出完整答案的,如果需要简历修改服务可以发我邮箱:zx597813039@gmail.com

ReferenceError: a is not defined

  1. 立即执行(async function() {a().b().c() })()。因为返回是一个promise,所以返回一个promise.reject
  2. 执行完接下来的同步任务,这时候执行完try...catch
  3. 执行promise.reject,报错
commented

答案已经公布,地址

感觉上述的答案并不对。
从 async/await 的语法糖实现来看,题中的 async 函数是放在new Promise(executorFunc)executorFunc函数中执行的,众所周知executorFunc是立即执行,所以不存在什么异步任务。
而错误为什么不被 try/catch 捕获,是因为 Promise 在实例化的时候会捕获executorFunc的错误,所以外面是捕获不到的。

感觉上述的答案并不对。
从 async/await 的语法糖实现来看,题中的 async 函数是放在new Promise(executorFunc)executorFunc函数中执行的,众所周知executorFunc是立即执行,所以不存在什么异步任务。
而错误为什么不被 try/catch 捕获,是因为 Promise 在实例化的时候会捕获executorFunc的错误,所以外面是捕获不到的。

我感觉你说的才是正确的,我昨天也是第一次知道Promise中的错误不会被抛到外部了。然后搜索一圈,就找到这么一篇文章翻译自V8引擎作者的文章 从JS引擎理解Await b()与Promise.then(b)的堆栈处理 。虽然不是和本次题目强关联,但也是在Promise与堆栈方面带给我所不知道的一面

try {
    let a = 0
    ;(async function() {
        a += 1
        console.log('inner', a)
        throw new Error('123')
        // a()
    })()
    console.log('outer', a)
} catch(e) {
    console.warn('Error', e)
}

如上代码,他是能够输出'outer' 1 的 ; try catch认为你这段代码没有错,所以进不了catch。Promise内部的错误应该在Promise中解决。
我想请教你有没有相关文章能够佐证这个观点。

;(function(){
    let p
    try {
        let a = 0
        p=Promise.resolve(1).then(e=>a())
        console.log('outer', a)
    } catch(e) {
        console.warn('Error', e)
    }
    setTimeout(()=>{
        p.catch(e=>{console.warn(e)})
    },1000)
})()

然后我有这么一段代码,我惊奇的发现chrome控制台会在1s后把错误输出给吞回去;输出warnning。
这是什么原理

commented

感觉上述的答案并不对。
从 async/await 的语法糖实现来看,题中的 async 函数是放在new Promise(executorFunc)executorFunc函数中执行的,众所周知executorFunc是立即执行,所以不存在什么异步任务。
而错误为什么不被 try/catch 捕获,是因为 Promise 在实例化的时候会捕获executorFunc的错误,所以外面是捕获不到的。

我感觉你说的才是正确的,我昨天也是第一次知道Promise中的错误不会被抛到外部了。然后搜索一圈,就找到这么一篇文章翻译自V8引擎作者的文章 从JS引擎理解Await b()与Promise.then(b)的堆栈处理 。虽然不是和本次题目强关联,但也是在Promise与堆栈方面带给我所不知道的一面

try {
let a = 0
;(async function() {
a += 1
console.log('inner', a)
throw new Error('123')
// a()
})()
console.log('outer', a)
} catch(e) {
console.warn('Error', e)
}
如上代码,他是能够输出'outer' 1 的 ; try catch认为你这段代码没有错,所以进不了catch。Promise内部的错误应该在Promise中解决。
我想请教你有没有相关文章能够佐证这个观点。

;(function(){
let p
try {
let a = 0
p=Promise.resolve(1).then(e=>a())
console.log('outer', a)
} catch(e) {
console.warn('Error', e)
}
setTimeout(()=>{
p.catch(e=>{console.warn(e)})
},1000)
})()
然后我有这么一段代码,我惊奇的发现chrome控制台会在1s后把错误输出给吞回去;输出warnning。
这是什么原理

这种问题其实其实查标准就好了:

内部 throw 被 promise reject 了,外部就不可能捕获到这个错误,当然不管有没有这个机制都是捕获不到的,毕竟异步代码怎么让同步代码捕获错误。

标准具体地址

如上图,从 ECMA-262 的27.7.5.1 AsyncFunctionStart一节来看,async 函数是立即执行的,执行结果出现异常时reject

  1. Set the code evaluation state of asyncContext such that when evaluation is resumed for that execution context the following steps will be performed:
    a. Let result be the result of evaluating asyncFunctionBody.
    ...
    d. If result.[[Type]] is normal, then
        i. Perform ! Call(promiseCapability.[[Resolve]], undefined, « undefined »).
    e. Else if result.[[Type]] is return, then
        i. Perform ! Call(promiseCapability.[[Resolve]], undefined, « result.[[Value]] »).
    f. Else,
        i. Assert: result.[[Type]] is throw.
        ii. Perform ! Call(promiseCapability.[[Reject]], undefined, « result.[[Value]] »).
    g. Return.

至于第二个问题,众所周知当 Promise 异常未被捕获时,会报Uncaught (in promise) TypeError,翻了下 ECMA-262 好像没有明确规定,延迟捕获异常要怎么办。Chrome 会吞回去,Firefox 并不会,大概是自行发挥的结果吧。
@cn1001wang

@ambit-tsai @KieSun 感谢二位, execution context stack 我还得再研究研究 !

commented

try{
(async function(){
await a().b().c()
})()
}catch(e){
console.log(e,'报错原因-------')
}

1 首先 try catch (同步)异常 报错 会执行 catch
2 async 是一个promise 语法糖 返回一个 promise
3 在 async 执行栈中 a b c 3个方法
4 在执行a的时候 会直接报错 a is nodefind
5 async 会返回 promise 其实在这里应该是有一个错误 但是 浏览器 读取到 a方法的错误, 到此为止 在不考虑任何因素的情况下 同步的捕获异常 肯定是捕获不到异步的方法里面的问题。

a is not defined

try {
    (async function() { a().b().c() })()
} catch (e) {
    console.log(`执行出错:${e.message}`)
}
  1. 执行出现异常 Uncaught (in promise) ReferenceError: a is not defined
  2. try catch 无法捕获异常
commented

游览器会抛出异常 a is not defined
因为try catch无法捕获异步方法中的错误

  1. 执行结果 Uncaught (in promise) ReferenceError: a is not defined
  2. try catch 无法捕获异常
  3. 需要看看大佬们的见解,我理解的不透测

个人觉得这道题考察的是 async 函数执行出错时语言层面是怎么处理的,而不是异步导致 try catch 无法捕获错误。(async function() { a().b().c() })() 并没有涉及到异步。

@ambit-tsai 的解释比较靠谱

  1. 结果为 Uncaught (in promise) ReferenceError: a is not defined
  2. try catch 是同步代码,无法捕捉异步代码中的错误
  3. 在 async 中,执行了未定义的 a(),因此这时候的错误被 Promise.reject 捕捉

a is not defined
函数前加上async相当于把它放到了promise的executor函数内,变成了异步
因为try catch没办法捕获到异步的错误,想要得到执行出错:a is not defined的结果需要把捕获写到函数内部
图片