js执行机制

概念及问题

JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊。

JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?

所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。

为了解决这个问题,利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程。于是,JS中出现了同步异步

同步任务

同步任务都在主线程上执行,形成一个执行栈,程序执行的时候,按照顺序依次执行

异步任务

异步任务是通过回调函数实现的,程序执行的时候,异步任务相关添加到任务队列中(任务队列也称为消息队列)。程序会调过某个步骤继续向下执行,一般而言,异步任务有以下三种类型:

1.普通事件:如click、resize等;

2.资源加载:如load、error等;

3.定时器:包括setInterval、setTimeout等;

事件循环

EventLoop

Event Loop即事件循环,是指浏览器或Node的一种解决javaScript单线程运行时不会阻塞的一种机制,也就是我们经常使用异步的原理。

描述了计算机在执行js时候的一个状态(先去执行栈中执行同步代码,然后再执行队列中异步代码)

异步任务相关添加到任务队列中(任务队列也成为消息队列)

一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取任务队列中的异步任务,于是被读取的异步任务结束等待状态,进行执行栈,开始执行。

异步任务分类

异步任务分为宏任务和微任务两种:

微任务处理完毕后才会处理宏任务,而该异步任务被判定为宏还是微取决于官方的定义。常见分类如下:

  • 宏任务:正常的异步任务都是宏任务,最常见的就是定时器(setInterval, setImmediate, setTimeout);script(整体代码) 、用户交互、IO任务;

  • 微任务:微任务出现比较晚,queueMicrotask、Promise的then和catch方法、catch、finally 

微任务生成方法:

Promise:Promise是一种异步编程的解决方案,它可以将异步操作封装成一个Promise对象,通过then方法注册回调函数,当promise变为resolve或者reject会将回调函数加入微任务队列中。

MutationObserver:MutationObserver是一种可以观察DOM变化的API,通过监听DOM变化事件并注册回调函数,将回调函数加入微任务队列中。

process.nextTick:process.nextTick是Node.js中的一个API,它可以将一个回调函数加入微任务队列中。

宏任务生成方法:

用户交互:用户在页面上进行交互操作(例如点击、滚动、输入等),会触发浏览器产生宏任务来响应用户操作。

网络请求:当浏览器发起网络请求(例如通过 Ajax、Fetch、WebSocket 等方式)时,会产生宏任务来处理请求和响应。

定时器:通过 JavaScript 宿主环境提供的定时器函数(例如 setTimeout、setInterval)可以设置一定的时间后产生宏任务执行对应的回调函数。

DOM 变化:当 DOM 元素发生变化时(例如节点的添加、删除、属性的修改等),会产生宏任务来更新页面。
跨窗口通信:在浏览器中,跨窗口通信(例如通过 postMessage 实现)会产生宏任务来处理通信消息。

JavaScript 脚本执行事件;比如页面引入的 script 就是一个宏任务。

执行顺序代码演示

setTimeout(function(){
  console.log(777)
},10)
console.log('000');//第一步

(async ()=>{
  console.log(111);//2
  await console.log(222);//3,此行执行,当await右边跟随的代码执行完毕的时候,才会执行后面的代码,此时后面的代码(后面3行)也可以算作“内部的微任务”,所以此行执行后执行输出333
  await fn()
  console.log(444);
  console.log(555);
})().then(()=>{
  console.log(666);
});
function fn(){
  console.log('a')
  return new Promise((res,rej)=>{
    console.log('b')
    res('d')
    console.log('c')
  })
}
console.log('333');//4

输出:

注意:

promise内部遇到resolve()和reject()调用的时候,会继续执行后面的代码,但是then和reject就直接放入为微任务队列中,等待同步任务执行

但async内部遇到await的时候,只有当await右边跟随的代码执行完毕的时候,才会执行后面的代码,此时后面的代码也可以算作内部的微任务(因为遇到await会将等await执行完成后,执行外面的同步任务,再执行await后面的任务,见下图)

setTimeout(function(){
  console.log(777)
},10)
console.log('000');//第一步

(async ()=>{
  console.log(111);//2
  await console.log(222);   //3,此行执行,当await右边跟随的代码执行完毕的时候,才会执行后面的代码,此时后面的代码(237-239行)也可以算作内部的微任务,所以此行执行后执行252行
  await fn()
  console.log(444);
  console.log(555);
})().then(()=>{
  console.log(666);
});
async function fn(a){
  console.log(a+'a')
  await console.log(222222)
  return new Promise((res,rej)=>{
    console.log(a+'b')
    res(a+'d')
    console.log(a+'c')
  })
}
fn(1).then((e)=>{
  console.log(e);
})
console.log('333');

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值