JavaScript事件循环

js 复制代码
async function async1() {
  console.log('async1 start');
  await async2();
  console.log('async1 end');
}

async function async2() {
  console.log('async2');
}

console.log('script start');
setTimeout(function() {
  console.log('setTimeout');
});

async1();
console.log('script start');
new Promise(function(resolve) {
  console.log('promise1');
  resolve();
}).then(function() {
  console.log('promise2');
});
输出:
script start
async1 start
async2
script start
promise1
async1 end
promise2
setTimeout

js是一个单线程,会阻塞代码

为了防止阻塞代码,把代码分为同步和异步

同步代码交给js引擎执行(js只能是单线程)

异步代码交给宿主环境(浏览器/node,可以支持多线程)

同步代码放到执行栈,异步代码等时机成熟会放到任务队列里

执行栈执行完,会去任务队列看是否有异步任务,有就推送到执行栈执行,反复循环查看执行,这个过程就是事件循环(eventloop)

1.同步任务和异步任务

1.定时器

js 复制代码
console.log(1)
setTimeout(() => {
  console.log(2)
}, 0)
console.log(3)
输出:132

js是单线程的,同一时间只能做一件事

作为浏览器脚本语言,与它的用途有关

js主要用途是和用户互动,以及操作DOM,这决定了它只能是单线程

例如如果线程1和线程2分别是添加和删除同一个节点,执行顺序不同会导致结果的不同

所以只能先添加再删除

总不能等计时器结束才执行吧,总不能等到事件监听点击之后才执行吧

异步代码的共同点:它们都是耗时的

同步代码放到执行栈,异步代码放到宿主环境

执行栈:里的代码立即执行,并且原地等待结果

宿主环境的时间一到,就推送到任务队列

执行栈执行完了,就会看任务队列有没有异步任务需要执行

此时再把任务队列的异步函数的回调函数推送到执行栈

执行栈只要同步代码一执行完,就会反复到任务队列去看有没有异步的任务需要执行

从执行栈到任务队列反复查找的过程就叫事件循环

2.异步任务(宏任务/微任务)

js把异步任务分为宏任务和微任务

ES5之后,js引入了Promise

这样,即使不需要浏览器,js引擎自身也能发起异步任务

宏任务由宿主(浏览器、Node)发起

微任务由JS引擎发起

主要的异步任务有Promise

不过Promise本身同步,里面的then/catch的回调函数是异步的

代码示例
js 复制代码
console.log(1);
setTimeout(function () {
    console.log(2);
}, 0);

const p = new Promise((resolve, reject) => {
    console.log(3);
    resolve(1000); // 标记为成功
    console.log(4);
});

p.then(data => {
    console.log(data);
});

console.log(5);
输出:1 3 4 5 1000 2
js 复制代码
new Promise((resolve, reject) => {
  resolve(1);


  new Promise((resolve, reject) => {
    resolve(2);
  }).then(data => {
    console.log(data);
    });
}).then(data => {
  console.log(data);
});

console.log(3);
输出:3 2 1
js 复制代码
console.log(11);

setTimeout(() => {
  console.log(12);
  let p = new Promise((resolve) => {
    resolve(13);
  });
  p.then(res => {
    console.log(res);
  });
}, 0);

console.log(15);

console.log(14);
输出:11 15 14 12 13
js 复制代码
setTimeout(() => {
  console.log(1);
}, 0);

new Promise((resolve, reject) => {
  console.log(2);
  resolve('p1');
})
new Promise((resolve, reject) => {
  console.log(3);
  setTimeout(() => {
    resolve('setTimeout2');
    console.log(4);
  }, 0);
  resolve('p2');
}).then(data => {
  console.log(data);
});

setTimeout(() => {
  resolve('setTimeout1');
  console.log(5);
}, 0).then(data => {
  console.log(data);
});

console.log(6);
js 复制代码
setTimeout(() => {
  console.log(1);
}, 0);

new Promise((resolve, reject) => {
  console.log(2);
  resolve('p1');

  new Promise((resolve, reject) => {
    console.log(3);
    setTimeout(() => {
      resolve('setTimeout2');
      console.log(4);
    }, 0);
    resolve('p2');
  }).then(data => {
      console.log(data);
})

  setTimeout(() => {
      resolve('setTimeout1');
      console.log(5);
  }, 0)
}).then(data => {
    console.log(data);
});

console.log(6);

输出:2 3 6 p2 p1 1 4 5
定时器里的resolve('setTimeout2')、resolve('setTimeout1')是迷惑性的
promise里有resolve,定时器里是不执行的

async函数里的代码属于同步任务,await后面所有的代码,在函数后面的所有代码都属于微任务,等代码执行完了才去执行

js 复制代码
async function async1() {
  console.log('async1 start');
  await async2(); //await下面的代码暂时是不执行的,除非右边全部执行完了才会去执行
  console.log('async1 end');
}

async function async2() {
  console.log('async2');
}

console.log('script start');

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

async1();
输出:script start/async1 start/async2/async1 end/setTimeout
相关推荐
艾小码2 分钟前
Vue组件通信不再难!这8种方式让你彻底搞懂父子兄弟传值
前端·javascript·vue.js
lcc1873 分钟前
Vue 数据代理
前端·javascript·vue.js
Moment4 分钟前
为什么我们从 Python 迁移到 Node.js
前端·后端·node.js
excel8 分钟前
📘 全面解析:JavaScript 时间格式化 API 实战指南
前端
咖啡の猫1 小时前
Vue基本路由
前端·vue.js·状态模式
青衫码上行1 小时前
【Java Web学习 | 第七篇】JavaScript(1) 基础知识1
java·开发语言·前端·javascript·学习
咖啡の猫1 小时前
Vue编程式路由导航
前端·javascript·vue.js
星释1 小时前
Rust 练习册 10:多线程基础与并发安全
开发语言·后端·rust
披着羊皮不是狼2 小时前
多用户博客系统搭建(1):表设计+登录注册接口
java·开发语言·springboot
zzzyyy5384 小时前
C++之vector容器
开发语言·c++