JS的单线程、同步异步任务

本文详细介绍了JavaScript为何采用单线程机制,以及同步与异步任务的概念,包括Ajax、定时器和事件监听等异步方式。通过事件循环和任务队列,解释了异步任务的执行顺序。此外,还讨论了宏任务和微任务的区别,以及它们在执行流程中的顺序。最后,通过示例展示了Promise和async/await在异步处理中的应用。
摘要由CSDN通过智能技术生成

一、为什么JS要采用单线程

    单线程就是同一时间只能干一件事。JS作为一门浏览器脚本语言,它主要的作用就是和用户交互,操作DOM。浏览器一次只分配给JS一个主线程,执行一个任务。如果同时有两个线程,一个线程在增添,另一个在删除,会造成混乱,所以JS只能是单线程的。

    为了利用CPU的多核,HTML5提出Web Worker标准,它允许JS脚本同时开启多个线程,但只能有一个主线程,其他分线程听从主线程并且不能操作DOM。

二、同步与异步任务

2.1 什么是同步异步任务

       同步任务指没有被JS引擎挂起,在主线程上执行的任务,只有前一个任务执行完,后一个任务才会执行。

       异步任务是指被JS引擎挂起,没有进入主线程,进入任务队列的任务,只有JS引擎认为该任务可以执行了(比如Ajax请求从服务器返回了结果),才会进入主线程执行(采用回调函数形式),在异步任务之后的同步任务不用等待异步任务执行完再执行,因此异步任务不具有阻塞效应。

      异步任务必须指定回调函数,在主线程上执行的就是对应的回调函数

2.2 哪些是异步任务

      主要有:Ajax网络请求,DOM事件监听,setTimeOut(),setInterval() 

      注意:alert()是同步任务,如果不点击alert弹出框的确定按钮,其后的代码就不会执行

2.3 如何实现异步任务

      任务队列:在JS引擎代码初始化阶段,会把包含回调函数的代码交给事件管理模块,当回调代码被触发执行(定时器时间到达指定时间,DOM事件被触发),放到任务队列里,等初始化代码(同步任务)执行完以后,就可以执行异步任务。

      事件循环(Event Loop):JS引擎在执行完同步任务后,每隔一段时间就会检查这些在任务队列里的代码能否执行,这种循环检查的机制叫做事件循环。

            

2.4 node.js的事件循环

node.js的运行机制:

(1)V8引擎解析JavaScript脚本。
(2)解析后的代码,调用Node API。
(3)libuv库负责Node API的执行。它将不同的任务分配给不同的线程,形成一个Event Loop(事件循环),以异步的方式将任务的执行结果返回给V8引擎。
(4)V8引擎再将结果返回给用户。

node.js也贡献了两个与任务队列相关的函数:process.nextTick(),setImmediate()

   process.nextTick(): 在当前执行栈的尾部,下一次事件循环之前执行回调函数,且内部包含的  代码一次执行完,它会在所有异步任务执行之前执行

   setImmediate(): 在当前任务队列的尾部添加一个回调函数,也就是加入的代码会在下一次事件循环时执行

小栗子1:


process.nextTick(function A() {
  console.log(1);
  process.nextTick(function B(){console.log(2);});
});

setTimeout(function timeout() {                //异步代码
  console.log('TIMEOUT FIRED');
}, 0)
// 1
// 2
// TIMEOUT FIRED

      process.nextTick()里的回调代码会在异步代码执行之前,同步代码执行完毕后调用,且内部的回调函数一次性执行完毕,所以2在TIMEOUT FIRED之前输出。

小栗子2:

setImmediate(function A() {
  console.log(1);
  setImmediate(function B(){console.log(2);});
});

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

  setTimeOut(fn,0)和setImmediate都是在下一次事件循环执行,但是谁先谁后呢?答案是不一定。

 这时输出结果可能为1,TIMEOUT FIRED,2或者TIMEOUT FIRED,1,2


setImmediate(function (){
  setImmediate(function A() {
    console.log(1);
    setImmediate(function B(){console.log(2);});
  });

  setTimeout(function timeout() {
    console.log('TIMEOUT FIRED');
  }, 0);
});
// 1
// TIMEOUT FIRED
// 2

   若将setTimeOut和setImmediate同时放在setImmediate函数内,这时setImmediate代码一定在setTimeOut(fn,0)之前执行。则执行结果一定是1,TIMEOUT FIRED,2,因为setImmediate是在下一轮事件循环中执行,1和TIMEOUT FIRED是一轮,2在它们之后的一轮。

三、宏任务与微任务

3.1 什么是宏任务、微任务

   宏任务和微任务是JS中处理等待任务队列中异步任务的机制

   JS里有等待任务队列、同步任务队列,同步任务队列处理同步任务

3.2 哪些算宏任务、微任务

   宏任务: 整体代码script,setTimeOut(),setInterval(),setImmediate()

   微任务:promise.then(),process.nextTick(),async定义的函数里await下面的代码,  Object.observe,Mutation.observer

3.4 执行顺序

   都遵循微任务优于宏任务

   首先JS会执行整体script代码,先执行同步代码,把宏任务放到宏任务队列中,微任务放到微任务队列中,执行完同步代码后,再从微任务队列依次取出微任务执行,执行完再依次从宏任务队列中取出宏任务执行,之后再执行微任务,宏任务,一直到执行完毕

   注意:如果遇到微任务中包含宏任务,会先执行完微任务,再来执行包含着的宏任务。

小栗子1:

    setTimeout(() => {
      console.log('1');
      new Promise(function (resolve, reject) {
        console.log('2');
        setTimeout(() => {
          console.log('3');
        }, 0);
        resolve();
      }).then(function () {
        console.log('4')                            //执行微任务,执行完执行任务队列里的宏任务,输出6
      })
    }, 0);
    console.log('5'); //5 7 10 8 1 2 4 6 3
    setTimeout(() => {                              //宏任务队列中该定时器优于输出3的定时器执行
      console.log('6');
    }, 0);
    new Promise(function (resolve, reject) {
      console.log('7');
      // reject();
      resolve();
    }).then(function () {
      console.log('8')
    }).catch(function () {
      console.log('9')
    })
    console.log('10');

小栗子2:

async定义的函数,会默认返回一个promise实例,如果不是,会将它包装成一个promise对象

let p;
async function f3() {
    p = await 18;
    console.log(p);
}

f3();
console.log(1);
console.log(p);

//  结果是1,undefined,18

函数体内console.log(p)在await后面,变成微任务,先执行函数体外console.log(1), console.log(p), p未定义,输出undefined.

百度机考题:

以下代码的运行结果是?
const promiseA = Promise.resolve('a')

promiseA.then((res) => {
    console.log(res)
}).then((res) => {
    console.log(res)
})

const promiseB = Promise.resolve('b')

promiseB.then((res) => {
    console.log(res)
})
promiseB.then((res) => {
    console.log(res)
})

//输出:a,b,b,undefined

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值