• 每一个任务都有一个重要特点:“执行至完成”(正常不会被终止)

一个误区:关于settimeout里的第二个参数,并不是说加入队列后开始计时,这个时间代表的是最小延迟时间,真正等待时间会比这个大,是碰到它的时候就开始计时,到时后加入宏任务队列,这里的计时器是由其他线程完成的。

The time value represents the (minimum) delay after which the message will be pushed into the queue. If there is no other message in the queue, and the stack is empty, the message is processed right after the delay.

第一句话的意思就是计时完成后加入宏任务队列

alt

一个 web worker 或者一个跨域的 iframe 都有自己的栈、堆和消息队列

异步代码实际上在主线程上运行(在其指定的计时器过去之后)

任何异步代码仅在主线程可用后才执行(换句话说,当调用栈为空时)

在 setTimeout() 调用执行之前或 setInterval() 迭代之间可以(并且经常会)运行其他代码。根据这些操作的处理器密集程度,它们可以进一步延迟异步代码。

递归setTimeout()和setInterval()有何不同?
  • 递归 setTimeout() 保证执行之间的延迟相同,例如在上述情况下为100ms。 代码将运行,然后在它再次运行之前等待100ms,因此无论代码运行多长时间,间隔都是相同的。
  • 使用 setInterval() 的示例有些不同。 我们选择的间隔包括执行我们想要运行的代码所花费的时间。假设代码需要40毫秒才能运行 - 然后间隔最终只有60毫秒。
  • 当递归使用 setTimeout() 时,每次迭代都可以在运行下一次迭代之前计算不同的延迟。 换句话说,第二个参数的值可以指定在再次运行代码之前等待的不同时间(以毫秒为单位)。

当你的代码有可能比你分配的时间间隔,花费更长时间运行时,最好使用递归的 setTimeout() - 这将使执行之间的时间间隔保持不变,无论代码执行多长时间,你不会得到错误。

永不阻塞

JavaScript的事件循环模型与许多其他语言不同的一个非常有趣的特性是,它永不阻塞。 处理 I/O 通常通过事件和回调来执行,所以当一个应用正等待一个 IndexedDB 查询返回或者一个 XHR 请求返回时,它仍然可以处理其它事情,比如用户输入。

关于宏任务和微任务的加入时间

当我们代码运行到settimeout或者promise时,并不是立刻加入的。 比如settimeout需要计时完成后加入宏任务队列,而promise必须在状态改变时(也就是执行resolve或者reject函数后),才会将then方法加入微任务队列。

当promise里嵌入settimeout时,情况会变得没那么简单,正常来说promise是同步的,但由于里面的代码被settimeout包裹后,里面的代码就不再是同步的了。

如下:

new Promise((resolve) => {
            console.log('1');
            setTimeout(() => { resolve(); console.log("5") }, 0);
})

推荐链接:

https://juejin.cn/post/6844903968292749319 https://www.jianshu.com/p/8821c6432fe1

写得很好哦


await被当作同步任务处理,其后的代码被作为微任务处理。


本文参考MDN