一、宏任务和微任务

宏任务和微任务是等待任务队列中的异步任务的处理机制,浏览器的任务队列中,主任务队列存储的都是同步任务,等待任务队列中存储的都是异步任务。

微任务:

  1. Promise 的 then 回调函数
  2. async 函数 await 下面的代码
  3. process.nextTick

宏任务:定时器(setInterval 和 setTimeout)

首先浏览器会把主动任务队列中的同步任务挨个全部执行完,然后再去等待任务队列中看那个任务可以执行了,然后把该执行的任务放到主任务队列中去执行,等这个任务执行完,再去等待任务队列中看谁可以执行了,再把这个任务放到主任务对了中去执行… 如此循环。这种循环叫做事件循环(Event Loop)

异步任务都是谁先到达条件谁先执行,但是谁先到达条件也有优先级的问题,这个优先级要看这个任务与是宏任务还是微任务,微任务的优先级高于宏任务。

  1. setTimeout(function () {
  2. console.log(1)
  3. }, 0);
  4. console.log(5);
  5. let p = new Promise((resolve, reject) => {
  6. console.log(2);
  7. resolve(); // resolve 执行并不是让 then 里面回调函数立即执行,resolve 执行只是告诉 then 的回调函数到达执行条件了;而 then 里面的回调函数异步执行的,需要等着所有的同步任务执行结束才能执行它;
  8. console.log(3);
  9. });
  10. console.log(6);
  11. p.then(() => {
  12. console.log(4)
  13. });
  14. console.log(7);
  15. // 5 2 3 6 7 【主任务队列中的同步任务都执行完了】 4 1

???为什么4先输出了

因为定时器都是宏任务,then 里面的回调函数是微任务,因为微任务的优先级比宏任务的高,所以微任务先执行,所以先输出4

当主任务队列中的同步任务执行完,先去等待任务队列中吧所有的微任务找到,并且执行这些微任务;执行完,再去吧宏任务找到并且执行

二、async 和 await

async 和 await 是 es6 新增的关键字,用于把异步变成同步

1. async

async 在函数定义时使用,用 async 定义的函数默认返回一个 Promise 实例,可以直接 .then

  1. async function fx () {
  2. console.log(1);
  3. }
  4. let obj = {
  5. async getName () {
  6. // async 还可以定义对象的方法
  7. }
  8. };
  9. fx().then(() => {
  10. // 因为 async 函数返回了 Promise 实例,所以可以 .then
  11. })
  12. function g() {
  13. return 'abc';
  14. }
  15. function h() {
  16. return new Promise((resolve, reject) => {
  17. setTimeout(() => {
  18. resolve('xyz');
  19. },300)
  20. })
  21. }

2. await (要和 async 一起使用)

  1. async function fn() {
  2. // await 等待,等右侧的代码执行完
  3. // await 用法:
  4. // let x = await g();
  5. // console.log(x);
  6. // 1. 如果 await 右侧是同步的代码,就会让同步代码执行,如果执行的是一个函数,还会把函数的返回值给到 await 左边的变量
  7. let y = await h() {
  8. console.log(y);
  9. console.log('123');
  10. }
  11. // h().then((res) => console.log(res)) 有了 async 和 await,就不用写 then 了;因为 await 可以取得 promise resolve 时传入的值
  12. // 2. 如果 await 右侧是一个 Promise 实例,或者一个方法返回了Promise 实例,await 会等着 Promise 的实例 resolve,并且在实例resolve 之前,await 后面的代码不执行;并且还会拿到 Promise 在 resolve 时传入的值,并且赋值给等号左侧变量;
  13. // 3. await 会把 await 下面的代码变成微任务;
  14. }
  15. fn();

真实项目中,async 和 await 常常结合 AJAX 和 Promise 一起使用
需求:用 async 和 await 改写先请求 login.json 再请求 banner.json 并且再请求结束后渲染到页面中;

  1. // await 后面常跟一个 Promise 实例或者跟返回的 Promise 实例的方法
  2. function getLogin() {
  3. return new Promise((resolve, reject) => {
  4. let xhr = new XMLHttpRequest();
  5. xhr.open('GET', 'login.json');
  6. xhr.onreadystatechange = function () {
  7. if (xhr.readyState === 4 && xhr.status === 200) {
  8. resolve(JSON.parse(xhr.responseText));
  9. }
  10. };
  11. xhr.send();
  12. });
  13. }
  14. function getBanner(token) {
  15. return new Promise((resolve, reject) => {
  16. let xhr = new XMLHttpRequest();
  17. xhr.open('GET', 'banner.json?token=' + token);
  18. xhr.onreadystatechange = function () {
  19. if (xhr.readyState === 4 && xhr.status === 200) {
  20. resolve(JSON.parse(xhr.responseText));
  21. }
  22. };
  23. xhr.send();
  24. })
  25. }
  26. async function bindHTML() {
  27. let login = await getLogin();
  28. async function bindHTML() {
  29. let login = await getLogin();
  30. // 从登录信息中取得 token,把 token 传给 getBanner; (第二个接口依赖第一个接口中的内容,用 await 等前面的接口响应完,就可以拿到数据,然后把你想要的数据从里面拿出来,再传给下一个请求)
  31. let { data: { token } } = login;
  32. let banner = await getBanner(token);
  33. console.log(login, banner);
  34. // 这下面可以执行绑定数据的操作
  35. // ....
  36. // 这里可以写开启轮播图的事情
  37. }
  38. bindHTML().then(() => {
  39. // 还可以在这个回调函数写开启轮播的事情
  40. })