Code前端首页关于Code前端联系我们

在浏览器中关注 大佬 - eventloop

terry 2年前 (2023-09-08) 阅读数 219 #Vue
console.log('script start');

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

Promise.resolve()
  .then(function () {
    console.log('promise1');
  })
  .then(function () {
    console.log('promise2');
  });

console.log('script end');
 
又过去一周了,第一篇文章好像是两周前的事了。如果你忘记了磐冲的jd描述,别忘了回顾一下第一个内容:
console.log('script start');

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

Promise.resolve()
  .then(function () {
    console.log('promise1');
  })
  .then(function () {
    console.log('promise2');
  });

console.log('script end');
 
1。关注大佬 - 深入理解js中的继承
console.log('script start');

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

Promise.resolve()
  .then(function () {
    console.log('promise1');
  })
  .then(function () {
    console.log('promise2');
  });

console.log('script end');
 
2。关注大佬——理解和使用闭包(附10个与闭包相关的面试问题)

清除:JavaScript 是一种单线程脚本语言。这意味着当执行一行代码时,不一定会同时执行另一行代码。

的定义
console.log('script start');

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

Promise.resolve()
  .then(function () {
    console.log('promise1');
  })
  .then(function () {
    console.log('promise2');
  });

console.log('script end');
 
事件循环定义

w3c 将事件循环定义为:对于事件协调、用户交互、脚本、渲染、网络等,用户代理必须使用事件循环。例如:当浏览器发起网络请求时,主程序收到异步信号后会先做其他事情。异步完成后,主程序会在空闲时处理异步内容。

事件定义

w3c 将事件定义为由于某些外部或内部信息的状态变化而产生的适当反应。示例:用户单击按钮。 该活动将包含多个任务

console.log('script start');

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

Promise.resolve()
  .then(function () {
    console.log('promise1');
  })
  .then(function () {
    console.log('promise2');
  });

console.log('script end');
 
事件循环机制

w3c 事件循环定义:事件循环具有一个或多个 作业队列 。任务队列是任务的有序列表。

♂ 每个作业都来自特定的 作业源,来自同一作业源并且属于特定事件循环的作业通常会添加到作业队列中,但来自不同作业源的作业可能会被放置在不同的作业队列。 每个事件循环都有一个进入检查点microtask的标志。该标志的初始值为 false,进入检查点microtask 的算法会被组织重复调用。

达到 根据各种JavaScript解释工具,js可以在浏览器节点中执行。 eventloop在浏览器中的具体实现是由各个浏览器厂商完成的,而node则使用libuv库来实现。
console.log('script start');

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

Promise.resolve()
  .then(function () {
    console.log('promise1');
  })
  .then(function () {
    console.log('promise2');
  });

console.log('script end');
 
这意味着浏览器事件循环和节点事件循环是两个概念。
console.log('script start');

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

Promise.resolve()
  .then(function () {
    console.log('promise1');
  })
  .then(function () {
    console.log('promise2');
  });

console.log('script end');
 
事件循环有很多来自不同作业源的作业队列,每个队列严格按照FIFO顺序执行,但不同作业队列的执行顺序不同,浏览器根据不同的调度方式进行不同的调度不同标准的作业队列。
console.log('script start');

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

Promise.resolve()
  .then(function () {
    console.log('promise1');
  })
  .then(function () {
    console.log('promise2');
  });

console.log('script end');
 
浏览器实现

在js中,任务分为两种:宏任务MacroTask和微任务MicroTask

其中有宏任务:

  • 脚本代码
  • 设置超时
  • 设置间隔
  • setImmediate(仅在IE10下)
  • I/O
  • 用户界面渲染
微任务包括:
  • Process.nextTick(仅限节点)
  • 承诺
  • Object.observer(已弃用)
  • 突变观察者

浏览器实现任务

console.log('script start');

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

Promise.resolve()
  .then(function () {
    console.log('promise1');
  })
  .then(function () {
    console.log('promise2');
  });

console.log('script end');
 
同一上下文,常见的执行顺序是:同步代码->微任务->宏任务。
console.log('script start');

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

Promise.resolve()
  .then(function () {
    console.log('promise1');
  })
  .then(function () {
    console.log('promise2');
  });

console.log('script end');
 
浏览器将继续按顺序从任务队列中检索任务执行。每个任务执行完毕后,都会检查微任务队列是否为空。如果不为空,则会一次性执行所有微任务,然后去任务队列中挑选下一个。任务的执行情况。

跟着大佬走——浏览器中的eventloop跟着大佬走——浏览器中的eventloop

console.log('script start');

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

Promise.resolve()
  .then(function () {
    console.log('promise1');
  })
  .then(function () {
    console.log('promise2');
  });

console.log('script end');
 
例如:
console.log('script start');

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

Promise.resolve()
  .then(function () {
    console.log('promise1');
  })
  .then(function () {
    console.log('promise2');
  });

console.log('script end');
 
console.log('script start');

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

Promise.resolve()
  .then(function () {
    console.log('promise1');
  })
  .then(function () {
    console.log('promise2');
  });

console.log('script end');
 
答案是什么,先想一想
console.log('script start');

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

Promise.resolve()
  .then(function () {
    console.log('promise1');
  })
  .then(function () {
    console.log('promise2');
  });

console.log('script end');
 





script start
script end
promise1
promise2
setTimeout
 
console.log('script start');

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

Promise.resolve()
  .then(function () {
    console.log('promise1');
  })
  .then(function () {
    console.log('promise2');
  });

console.log('script end');
 
怎么样,和你想的一样吗?
解释运行顺序:

第1步:最初,只有脚本排队,所有脚本函数都放在执行堆栈上。当遇到setTimeout时,将其放入任务队列中,将在下一个事件循环中执行

step2:遇到时,将其放入微任务队列中。

第3步:脚本执行后,检查微任务队列,如果不为空则执行.then。由于仍然承诺第一个then的返回,因此将第二个then放入微任务队列中继续执行。

第4步:此时微任务队列为空,则进入下一个事件循环,任务队列一发现setTimeout就执行。

思考题

new MutationObserver(
  function() {
  console.log('mutate');
}).observe(outer, {
  attributes: true
});

function onClick() {
  console.log('click');

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

  Promise.resolve().then(
  function() {
    console.log('promise');
  });

  outer.setAttribute('data-random', Math.random());
}

inner.addEventListener(
  'click',
  onClick
);
outer.addEventListener(
  'click',
  onClick
);
 
考虑一下这道题的执行顺序?浏览背景并与我们讨论!

参考:

  • segmentfault.com/a/119000001…
  • jakearchibald.com/2015/tasks-…
  • segmentfault.com/a/119000001…

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门