Git Product home page Git Product logo

Comments (4)

shen-zhao avatar shen-zhao commented on August 17, 2024 1

对于后面两个案例我是这样的理解@LuckyWinty

首先讨论讨论一下async函数的返回值,我们都知道是一个新的promise对象,而不是真正函数体里return的那个值,但是不同值的类型会影响返回promise对象的行为,分两种情况:

1.返回一个非promise类型的任何值,此时返回的promise对象会把返回值作为作为resolve的结果,根据案例结果,这个promise立即被加入到了微任务队列

Promise.resolve(value);

2.返回一个promise类型的值,此时返回的promise对象会继承返回值的状态和值,类似于promise的链式调用,当链条中上游的promise的回调没有执行(执行时会resolve下游的promise),下游的promise还是pending状态!上游执行后,下游promise任务会追加到微任务队列尾部
根据案例中的结果,这个promise没有立即添加到微任务队列中,是因为它所依赖的promise回调还没有执行(异步)

参照以下代码:

Promise.resolve().then(() => {
  console.log('promise 1');
}).then(() => {
  console.log('promise 2');
});

Promise.resolve().then(() => {
  console.log('promise 3');
});
// promise 1 => promise 3 => promise 2

个人观点:
上述async/await的不同行为都可以转成promise理解
(说的有问题大家指正!!!)

from fe-weekly-questions.

LuckyWinty avatar LuckyWinty commented on August 17, 2024

JavaScript代码的执行过程中,除了依靠函数调用栈来搞定函数的执行顺序外,还依靠任务队列(task queue)来搞定另外一些代码的执行。整个执行过程,我们称为事件循环过程。一个线程中,事件循环是唯一的,但是任务队列可以拥有多个。任务队列又分为macro-task(宏任务)与micro-task(微任务),在最新标准中,它们被分别称为task与jobs。

macro-task大概包括:

  • script(整体代码)
  • setTimeout
  • setInterval
  • setImmediate
  • I/O
  • UI render

micro-task大概包括:

  • process.nextTick
  • Promise
  • Async/Await(实际就是promise)
  • MutationObserver(html5新特性)

整体执行,我画了一个流程图:

GitHub

总的结论就是,执行宏任务,然后执行该宏任务产生的微任务,若微任务在执行过程中产生了新的微任务,则继续执行微任务,微任务执行完毕后,再回到宏任务中进行下一轮循环。举个例子:
GitHub

结合流程图理解,答案输出为:async2 end => Promise => async1 end => promise1 => promise2 => setTimeout
但是,对于async/await ,我们有个细节还要处理一下。如下:

async/await执行顺序

我们知道async隐式返回 Promise 作为结果的函数,那么可以简单理解为,await后面的函数执行完毕时,await会产生一个微任务(Promise.then是微任务)。但是我们要注意这个微任务产生的时机,它是执行完await之后,直接跳出async函数,执行其他代码(此处就是协程的运作,A暂停执行,控制权交给B)。其他代码执行完毕后,再回到async函数去执行剩下的代码,然后把await后面的代码注册到微任务队列当中。我们来看个例子:

console.log('script start')

async function async1() {
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2 end')
}
async1()

setTimeout(function() {
console.log('setTimeout')
}, 0)

new Promise(resolve => {
console.log('Promise')
resolve()
})
.then(function() {
console.log('promise1')
})
.then(function() {
console.log('promise2')
})

console.log('script end')
// script start => async2 end => Promise => script end => promise1 => promise2 => async1 end => setTimeout

分析这段代码:

  • 执行代码,输出script start
  • 执行async1(),会调用async2(),然后输出async2 end,此时将会保留async1函数的上下文,然后跳出async1函数。
  • 遇到setTimeout,产生一个宏任务
  • 执行Promise,输出Promise。遇到then,产生第一个微任务
  • 继续执行代码,输出script end
  • 代码逻辑执行完毕(当前宏任务执行完毕),开始执行当前宏任务产生的微任务队列,输出promise1,该微任务遇到then,产生一个新的微任务
  • 执行产生的微任务,输出promise2,当前微任务队列执行完毕。执行权回到async1
  • 执行await,实际上会产生一个promise返回,即
let promise_ = new Promise((resolve,reject){ resolve(undefined)})

执行完成,执行await后面的语句,输出async1 end

  • 最后,执行下一个宏任务,即执行setTimeout,输出setTimeout

注意

新版的chrome浏览器中不是如上打印的,因为chrome优化了,await变得更快了,输出为:

// script start => async2 end => Promise => script end => async1 end => promise1 => promise2 => setTimeout

但是这种做法其实是违法了规范的,当然规范也是可以更改的,这是 V8 团队的一个 PR ,目前新版打印已经修改。
知乎上也有相关讨论,可以看看 https://www.zhihu.com/question/268007969

我们可以分2种情况来理解:

  1. 如果await 后面直接跟的为一个变量,比如:await 1;这种情况的话相当于直接把await后面的代码注册为一个微任务,可以简单理解为promise.then(await下面的代码)。然后跳出async1函数,执行其他代码,当遇到promise函数的时候,会注册promise.then()函数到微任务队列,注意此时微任务队列里面已经存在await后面的微任务。所以这种情况会先执行await后面的代码(async1 end),再执行async1函数后面注册的微任务代码(promise1,promise2)。

  2. 如果await后面跟的是一个异步函数的调用,比如上面的代码,将代码改成这样:

console.log('script start')

async function async1() {
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2 end')
return Promise.resolve().then(()=>{
  console.log('async2 end1')
})
}
async1()

setTimeout(function() {
console.log('setTimeout')
}, 0)

new Promise(resolve => {
console.log('Promise')
resolve()
})
.then(function() {
console.log('promise1')
})
.then(function() {
console.log('promise2')
})

console.log('script end')

输出为:

// script start => async2 end => Promise => script end => => promise1 => promise2 => async1 end setTimeout

此时执行完awit并不先把await后面的代码注册到微任务队列中去,而是执行完await之后,直接跳出async1函数,执行其他代码。然后遇到promise的时候,把promise.then注册为微任务。其他代码执行完毕后,需要回到async1函数去执行剩下的代码,然后把await后面的代码注册到微任务队列当中,注意此时微任务队列中是有之前注册的微任务的。所以这种情况会先执行async1函数之外的微任务(promise1,promise2),然后才执行async1内注册的微任务(async1 end).
可以理解为,这种情况下,await 后面的代码会在本轮循环的最后被执行.

from fe-weekly-questions.

LuckyWinty avatar LuckyWinty commented on August 17, 2024

对于后面两个案例我是这样的理解@LuckyWinty

首先讨论讨论一下async函数的返回值,我们都知道是一个新的promise对象,而不是真正函数体里return的那个值,但是不同值的类型会影响返回promise对象的行为,分两种情况:

1.返回一个非promise类型的任何值,此时返回的promise对象会把返回值作为作为resolve的结果,根据案例结果,这个promise立即被加入到了微任务队列

Promise.resolve(value);

2.返回一个promise类型的值,此时返回的promise对象会继承返回值的状态和值,类似于promise的链式调用,当链条中上游的promise的回调没有执行(执行时会resolve下游的promise),下游的promise还是pending状态!上游执行后,下游promise任务会追加到微任务队列尾部
根据案例中的结果,这个promise没有立即添加到微任务队列中,是因为它所依赖的promise回调还没有执行(异步)

参照以下代码:

Promise.resolve().then(() => {
  console.log('promise 1');
}).then(() => {
  console.log('promise 2');
});

Promise.resolve().then(() => {
  console.log('promise 3');
});
// promise 1 => promise 3 => promise 2

个人观点:
上述async/await的不同行为都可以转成promise理解
(说的有问题大家指正!!!)

也可以吧,就是下游promise任务会追加到微任务队列尾部,感觉怪怪的,队列只有入队吧,没有直接加尾部一说吧

from fe-weekly-questions.

sfsoul avatar sfsoul commented on August 17, 2024

对于后面两个案例我是这样的理解@LuckyWinty

首先讨论讨论一下async函数的返回值,我们都知道是一个新的promise对象,而不是真正函数体里return的那个值,但是不同值的类型会影响返回promise对象的行为,分两种情况:

1.返回一个非promise类型的任何值,此时返回的promise对象会把返回值作为作为resolve的结果,根据案例结果,这个promise立即被加入到了微任务队列

Promise.resolve(value);

2.返回一个promise类型的值,此时返回的promise对象会继承返回值的状态和值,类似于promise的链式调用,当链条中上游的promise的回调没有执行(执行时会resolve下游的promise),下游的promise还是pending状态!上游执行后,下游promise任务会追加到微任务队列尾部
根据案例中的结果,这个promise没有立即添加到微任务队列中,是因为它所依赖的promise回调还没有执行(异步)

参照以下代码:

Promise.resolve().then(() => {
  console.log('promise 1');
}).then(() => {
  console.log('promise 2');
});

Promise.resolve().then(() => {
  console.log('promise 3');
});
// promise 1 => promise 3 => promise 2

个人观点:
上述async/await的不同行为都可以转成promise理解
(说的有问题大家指正!!!)

你理解的很对啊~

from fe-weekly-questions.

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.