Giter Site home page Giter Site logo

Comments (27)

cduyzh avatar cduyzh commented on May 22, 2024 27

输出结果:

inner 1
outer 1
(node:1128) UnhandledPromiseRejectionWarning: Error: 123

原因:

async定义了一个会返回promise的异步函数,此时作为宏任务执行,内部也是同步执行,直到抛出了异常被reject了,可以看做异步函数的回调,进入了微任务的队列中。
这时宏任务队列里还有console.log('out' ,a)未执行,执行完后再去执行微任务里的回调即throw new Error('123')

inner的a输出1很好理解,outer的a输出1是因为,在打印的时候 已经执行过a+=1了,异步方法内部形成了闭包,导致a能访问到外部变量a

新问题回答:

async定义的方法默认一定会返回一个promise,这个promise要么会通过一个由async函数返回的值被resolve,要么会通过一个从async函数中抛出的(或其中没有被捕获到的)异常被reject。如果一个async函数的返回值看起来不是promise,那么它将会被隐式地包装在一个promise中。
所以它内部自动捕获异常,async函数执行中断,并通过隐式返回Promise将错误传递给调用者

有一个例子方便理解事件循环:微任务和宏任务

setTimeout(() => alert("timeout"));

Promise.resolve()
  .then(() => alert("promise"));

alert("code");
  • code 首先显示,因为它是常规的同步调用。
  • promise 第二个出现,因为 then 会通过微任务队列,并在当前代码之后执行。
  • timeout 最后显示,setTimeout是一个宏任务,并且回调的函数会在下次事件循环中执行宏任务前执行。
  1. 从 宏任务 队列(例如 “script”)中出队(dequeue)并执行最早的任务。
  2. 执行所有 微任务:
  3. 当微任务队列非空时:
  4. 出队(dequeue)并执行最早的微任务。
  5. 执行渲染,如果有。
  6. 如果宏任务队列为空,则休眠直到出现宏任务。
  7. 转到步骤 1。

from all-of-frontend.

pengpeng9413 avatar pengpeng9413 commented on May 22, 2024 9

我感觉最多👍 的没有点出大家疑惑的核心问题,宏任务和微任务就不说了,上一题已经解释的很清楚了,catch 这里肯定不会进去!
这里主要解释的问题是:throw new Error('123')
这个错误的抛出为啥在浏览器中执行中,会是最后一个?
明明和console.log("inner", a);在同一个执行上下文啊 ?
这里 @ ScarecrowG 答案是我觉得很好的答案:
重点(敲黑板)

  1. async + function是返回promise,当执行到throw new Error('123')时,抛出错误,函数执行中断,错误被隐式Promise.reject()接收,进入回调,这里被推入到一个新的事件循环的 微任务队列,注意这里的是新的一轮事件循环开始了。
  2. 执行前一轮事件循环的宏任务:console.log('outer', a)
  3. 执行完前一轮的宏任务,勤劳的浏览器又要关注stack 里的任务了,于是浏览器抛出
    Uncaught (in promise) Error: 123
  4. 理解该题async funtion 里面的是同步代码,回调才是“异步”,所以 console.log('inner', a) 最先打印。
  5. async 是 Generator的语法糖,部署了symbol.interator , 但是无论如何都逃离不了js 是线程,必须要靠回调解决异步任务的本
    质。
  6. 很多人学习宏任务和微任务,并没有关注宿主环境,没有思考为啥会有宏任务和微任务之分,都是任务按顺行执行不好么?
    这就涉及到我们的宿主环境和js 引擎如何合作的问题了,抛开宿主环境,干扯什么宏任务微任务怎么也扯不干净,这时候
    我们要需要一个更大的宏观视角,向上溯源,为什么会有这套机制(浏览器是如何工作的,好了,扯远了):
    在S3版本以及之前的版本,js 本身是没有异步执行的能力的,只能顺序执行,简单粗暴挺好么,这时候的合作方式是:
    浏览器把一段代码交给JS引擎,引擎按顺序执行,此时任务是浏览器发起的,老前端🐶们还记得ajax么,那么ajax 又是个啥玩
    意儿呢,核心在于XMLHttpRequest对象,而XMLHttpRequest是浏览器提供的对象哦。所以以前异步任务的执行是这样的“刀
    耕火种”,感兴趣的可以了解js,和浏览器的发展历史(本人有个大胆的想法,写一本web前端的前世今生)
    es5后有了Promise 之后,JS 引擎自身可以发起任务了,这里聊到我们的重点:
    我们把宿主发起的任务称为宏观任务,把 JavaScript 引擎发起的任务称为微观任务
    简而言之:就是反复“等待 - 执行”,周而复始。
    我们继续回答之前的疑惑,为啥有宏任务和微任务之分,答案在于:js引擎也可以发起异步任务了为了浏览器可以正常工作
    (谁叫JS是单线程语言呢)。
    image
    看着有点像洋葱模型对不对,但是其实和洋葱模型是有差别的,这里很多文章说微任务总是比宏任务先执行,这种调调不知道
    祸害了多少前端er, 正确的论述应该是:一个宏任务是包含许多微任务队列的,微任务总会在下一个宏任务之前执行。

from all-of-frontend.

yxlazy avatar yxlazy commented on May 22, 2024 3

inner 1
outer 1
Uncaught (in promise) Error: 123

  • 被async修饰的function具有异步特征,但总体上会按着同步代码的方式求值,因此a += 1会影响后面的a的值。
  • 由throw抛出的错误是异步的无法在同步的catch中被捕获到,所以不会执行catch块

from all-of-frontend.

secret344 avatar secret344 commented on May 22, 2024 3

async作为一个语法糖,内部不碰到await 都是同步执行,promise的错误传播是不会冒泡的,所以外层捕获不到内层,作为语法糖只需要添加await就可以传播异常。inner 1 outher 1 throw。第一次整理答题,说的可能不对,见谅。

from all-of-frontend.

KieSun avatar KieSun commented on May 22, 2024 2

输出结果:

inner 1
outer 1
(node:1128) UnhandledPromiseRejectionWarning: Error: 123

原因:

async定义了一个会返回promise的异步函数,此时作为宏任务执行,内部也是同步执行,直到抛出了异常被reject了,可以看做异步函数的回调,进入了微任务队列中,因为是微任务,
这时宏任务队列里还有console.log('out' ,a)未执行,执行完后再去执行微任务里的回调即throw new Error('123')

inner的a输出1很好理解,outer的a输出1是因为,在打印的时候 已经执行过a+=1了,异步方法内部形成了闭包,导致a能访问到外部变量a

新问题回答:

async定义的方法默认一定会返回一个promise,这个promise要么会通过一个由async函数返回的值被resolve,要么会通过一个从async函数中抛出的(或其中没有被捕获到的)异常被reject。如果一个async函数的返回值看起来不是promise,那么它将会被隐式地包装在一个promise中。
所以它内部自动捕获异常,async函数执行中断,并通过隐式返回Promise将错误传递给调用者

有一个例子方便理解事件循环:微任务和宏任务

setTimeout(() => alert("timeout"));

Promise.resolve()
  .then(() => alert("promise"));

alert("code");
  • code 首先显示,因为它是常规的同步调用。
  • promise 第二个出现,因为 then 会通过微任务队列,并在当前代码之后执行。
  • timeout 最后显示,setTimeout是一个宏任务,并且回调的函数会在下次事件循环中执行宏任务前执行。
  1. 从 宏任务 队列(例如 “script”)中出队(dequeue)并执行最早的任务。
  2. 执行所有 微任务:
  3. 当微任务队列非空时:
  4. 出队(dequeue)并执行最早的微任务。
  5. 执行渲染,如果有。
  6. 如果宏任务队列为空,则休眠直到出现宏任务。
  7. 转到步骤 1。

老哥,你是第一个完整答案的,需要简历修改的话直接微信群里和我说就行

from all-of-frontend.

tuhongwei avatar tuhongwei commented on May 22, 2024 1

Fucking Awesome !

inner 1
outer 1
- VM489:6 Uncaught (in promise) Error: 123
  • async 是 Promise 的语法糖,属于微任务,优先执行异步任务,输出 inner 1
  • 随即在该异步任务内 throw new Error('123')
  • 此时 console.log('outer', a) 取到的 a 是 1
  • 最后捕获异常异步代码主动抛出的异常

很奇怪,为什么 console.log('outer', a) 取到的 a 是 1 ???

async并不是Promise的语法糖,它是Generator 函数的语法糖

from all-of-frontend.

wanCheng7 avatar wanCheng7 commented on May 22, 2024 1

输出结果:

  • inner 1
  • outer 1
  • Uncaught (in promise) Error: 123

个人理解:

  • 由于async后面的函数会返回一个promise,所以catch不到异步报错,catch里的东西不会打印
  • try里面的代码,首先执行的是宏任务console.log('inner', a)
  • 然后是宏任务console.log('outer', a)
  • 最后才是微任务promise的reject,throw new Error('123')

from all-of-frontend.

ming556 avatar ming556 commented on May 22, 2024

inner 1
outer 1
Uncaught (in promise) Error: 123
先调用异步方法,a+=1,输出1,同时输出outer 1,随后异步方法抛出异常123

from all-of-frontend.

turkyden avatar turkyden commented on May 22, 2024

Fucking Awesome !

inner 1
outer 1
- VM489:6 Uncaught (in promise) Error: 123
  • async 是 Generator 的语法糖,返回一个 Promise,属于微任务,优先执行异步任务,输出 inner 1
  • 随即在该异步任务内 throw new Error('123')
  • 此时 console.log('outer', a) 取到的 a 是 1
  • 最后捕获异常异步代码主动抛出的异常

很奇怪,为什么 console.log('outer', a) 取到的 a 是 1 ???

from all-of-frontend.

turkyden avatar turkyden commented on May 22, 2024
  • 被async修饰的function具有异步特征,但总体上会按着同步代码的方式求值,因此a += 1会影响后面的a的值。
let a = 0
;(async function() {
   window.setTimeout(() => {
     a += 1
    console.log('inner', a)
   }, 1000)
})()
console.log('outer', a)

这样的话 outer a 应该是 0 吧?@yanxiaolazy

from all-of-frontend.

yukailis avatar yukailis commented on May 22, 2024

inner 1
outer 1
Uncaught (in promise) Error: 123

宏任务进入宏任务队列,微任务进入微任务队列。
代码当做宏任务执行,先执行自执行函数,打印inner,1;
async修饰的函数同步代码执行异步操作,throw new Error会当做异步代码执行,进入微任务队列;
然后继续执行当前宏任务,因为async代码同步执行问题,打印outer,2;
当前宏任务执行完毕,try cathch 只能捕获同步代码的抛错,走不到 catch 里面,最后执行throw new Error('123')

from all-of-frontend.

m-yangyu avatar m-yangyu commented on May 22, 2024

结果

  • inner 1
  • outer 1
  • Uncaught (in promise) Error: 123

为什么

首先执行自执行函数中的console, 然后会执行到thorw方法, 但是这个方式是一个被async function, 所以他的抛错会被延迟到微任务的结束,也就是后面的console会优先执行,执行完成之后才会去执行thorw抛出的错误,并且因为是一个微任务的原因,所以try...catch无法捕获

from all-of-frontend.

XINXINP avatar XINXINP commented on May 22, 2024

inner 1
outer 1
Uncaught (in promise) Error: 123
1.js代码的执行顺序
2.try catch 只捕获到同步代码,捕获不到promise in里的错误
3.;(function(){})()这里走的是同步,且a的作用域就是外面a的作用域

from all-of-frontend.

HydratedPig avatar HydratedPig commented on May 22, 2024

chrome执行结果如下:
image
知识点: async 作为Generator的语法糖,async自带执行器,它返回一个Promise对象,可以用then调用用catch捕获

  • Promise内部为同步代码,先执行 a += 1此时a === 1,输出inner 1
  • throw new Error('123')会被reject接收,丢进微任务队列里(不会打断当前宏任务的执行)
  • 执行console.log('outer', a)输出outer 1
  • 同步任务执行结束,没有error被catch
  • 执行微任务,微任务队列里有被reject接收的throw new Error('123'),报错Uncaught (in promise) Error: 123

如果把注释// a()去掉执行结果和原先一样,因为throw new Error('123')导致Promise内同步代码中断,不会执行a()

当然我们也可以这样改
image

from all-of-frontend.

tuhongwei avatar tuhongwei commented on May 22, 2024

执行结果如下:
inner 1
outer 1
Uncaught (in promise) Error: 123
首先执行async函数体会输出inner 1
当遇到throw时,会抛出一个异步的错误,所以后面的outer 1仍会执行,并不会被同步的catch捕获
抛出一个异步的错误之后,没有被捕获,所以最终会出现Uncaught (in promise) Error: 123错误

from all-of-frontend.

Timo534 avatar Timo534 commented on May 22, 2024

执行结果

inner 1
outer 1
Uncaught Error: 123

理由

可以把题目中的代码转化成下面代码

try {
    let a = 0
    new Promise((resolve, reject) => {
      a += 1
      console.log('inner', a)
      reject('123')
    })
    console.log('outer', a)
} catch(e) {
    console.warn('Error', e)
}

由于try{}catch(){}只能捕获同步代码的异常,因此无法捕获异步代码Promise中抛出的错误,所以最后会抛出Uncaught Error

新问题:async 是返回一个 Promise 对象,为啥函数体内的抛错能被返回的 Promise 捕获到

转换过后的代码刚好解答了这个问题

from all-of-frontend.

Ecaknight avatar Ecaknight commented on May 22, 2024

inner 1
outer 1
Error 123

from all-of-frontend.

muzishuiji avatar muzishuiji commented on May 22, 2024

inner 1
outer 1
VM61:6 Uncaught (in promise) Error: 123
at :6:15
at :8:7

async 返回的是一个Promise对象,其内部跑抛出的错误,无法被try...catch捕获. 而async定义的function的内部代码是同步的,所以会顺序执行先打印 inner 1,然后执行console语句打印 outer 1.接着遇到throw语句抛出错误.

from all-of-frontend.

littleKnave avatar littleKnave commented on May 22, 2024
  1. 执行立即执行函数async,函数里面await之前是同步的,执行console.log('inner', a)。所以输出inner 1
  2. async执行到throw new Error('123'),但是async返回是一个promise,所以相当于返回promise.reject('123')
  3. 执行console.log('outer', a),输出outer 1
  4. 同步函数没有异常,所以catch没有捕获异常console.warn('Error', e)没有执行
  5. 第2步的返回promise.reject('123')是异步的,所以继续执行promise.reject('123'),没有catch捕获,所以直接打印异常

from all-of-frontend.

kevin9281 avatar kevin9281 commented on May 22, 2024

输出结果:

inner 1
outer 1
Uncaught (in promise) Error: 123

回答:

  1. 先执行宏任务,执行完毕后,立即执行当前微任务队列中的所有微任务
  2. async 定义的方法 方法内没有await 都是同步执行 内部形成闭包
  3. console.log('inner', 1) 然后返回一个promise对象 抛出一个异常 123
  4. 执行 console.log('outer', 1)
  5. promise 内部抛出的错误 throw error 不会被外面的try catch捕获到

from all-of-frontend.

startfromscratch avatar startfromscratch commented on May 22, 2024

try {
let a = 0;
(async function() {
a += 1
console.log('inner', a)
throw new Error('123')
console.log('会执行么')
})()
console.log('outer', a)
setTimeout(()=>{
console.log(a,'定时器')
},)
} catch(e) {
console.warn('Error', e)
}

把题目补了一些细节

正常看到 这么简单呐 大概是 先执行 inner 1 接着 Erroe js不往下执行了,catch 报错 123

但是 它不讲理

在来看下补充的 添加了一个定时器 以及 throw 后继续打印了一个 log

实际上执行的结果是 inner 1 outer 1 以及 报错 123 最后是 1 定时器

看看为什么会这样

外层先不解释 直接看 async 里面的 async 是promise 语法糖 返回的是一个promise 在async 里面 抛出异常影响的是 promise promise 是异步 所有的异步里面 任意 抛出类似异常都不会影响 外层的执行 但是内部的执行一定会终止 ,可以改造一下 在async 前面 异步变同步 await 只要成同步 后续都会终止。

每一个宏任务都有自己的微任务 影响的只有自己 内部 而不会影响到外部。

from all-of-frontend.

yancongwen avatar yancongwen commented on May 22, 2024
try {
    let a = 0
    ;(async function() {
        a += 1
        console.log('inner', a)
        throw new Error('123')
    })()
    console.log('outer', a)
} catch(e) {
    console.warn('Error', e)
}

和下面的代码是等价的:

try {
    let a = 0
    ;(function() {
        return new Promise((resolve, reject) => {
            a += 1
            console.log('inner', a)
            reject(new Error('123'))
        })
    })()
    console.log('outer', a)
} catch(e) {
    console.warn('Error', e)
}

又可以简化为:

try {
    let a = 0
    new Promise((resolve, reject) => {
        a += 1
        console.log('inner', a)
        reject(new Error('123'))
    })
    console.log('outer', a)
} catch(e) {
    console.warn('Error', e)
}

答案

inner 1
outer 1
Uncaught (in promise) Error: 123

涉及到的知识点

  • async函数一定会返回一个promise对象,如果一个async函数的返回值看起来不是promise,那么它将会被隐式地包装在一个promise中;
  • try catch 无法捕获异步任务中的异常,不管是微任务还是宏任务;
  • promise 中的异常(reject)只能使用 Promise.prototype.catch 捕获;
  • promise 中的代码是同步任务,then 和 catch 中的代码才是异步;
  • JavaScript 中的事件循环机制:
    • 首先执行宏任务;
    • 执行过程中,遇到的微任务和宏任务将被挂起,放入任务队列;
    • 宏任务执行完成之后,执行被挂起的微任务;
    • 检查任务队列中的宏任务,执行;
    • 循环上述流程;

from all-of-frontend.

wuqiren avatar wuqiren commented on May 22, 2024

基础有点差,我居然认为立即执行函数形成一个独立的作用域,然后就没办法访问外部变量,自认为输出一个 a is not defined。
立即执行函数的作用是创建一个独立的作用域,这个作用域的变量,外部无法访问

from all-of-frontend.

noshower avatar noshower commented on May 22, 2024

这道题目,前端基础不好的人,看别人的回答容易被人误导,分辨不出谁对谁错。

最好的方式是看规范。

还有就是 尝试手写 Promise ,手写 async await 的实现。

其次也可以看 babel 编译后的代码。

这道题目的关键是:Promise 内部的报错去哪了?

from all-of-frontend.

Turing-ld avatar Turing-ld commented on May 22, 2024

答案:

inner 1
outer 1
(node:18196) UnhandledPromiseRejectionWarning: Error: 123

跑去复习一波js的执行机制,直接自信。
async看着有点难受,改一改:

try {
  let a = 0;
  function func() {
    return new Promise((resolve, reject) => {
      a += 1;
      console.log('inner', a);
      throw new Error('123')
    });
  }
  func(); // (1)
  console.log('outer', a);
} catch (e) {
  console.log('出错了:', e);
}

疑惑:Promise构造器的回调还是同步的,那么抛出的错误应该会中止整个代码的呀

// 我把上述代码(1)处改成
func().catch(err => {
  console.log(err) // 123
})

懂了,throw被隐式reject出去了。所以这个抛出错误的代码被当作微任务,整段js执行(第一次宏任务)后才开始执行微任务。
至于为啥不会被try catch捕获,因为她只能捕获同步任务的,微任务属于异步。

from all-of-frontend.

DaisyX-BOT avatar DaisyX-BOT commented on May 22, 2024

inner 1
outer 1
Uncaught (in promise) Error 123

from all-of-frontend.

rookielzy avatar rookielzy commented on May 22, 2024

从上一个问题中学习到 try catch 无法捕捉异步代码错误,因此这里的输出为:

  1. inner 1
  2. outer 1
  3. 抛出错误 123(并不是 try catch 捕捉的)

from all-of-frontend.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.