一篇文章带你搞懂面试常考点-Promise与事件循环

210 阅读4分钟

前言:

JavaScript是一个单线程的语言,因此它并不能像Java一样并发地执行2多个任务。只能一个接一个地按照JavaScript规定好的顺序去执行。

但我们都知道的是有些程序(定时器、AJAX请求等)是需要一定时间等待后才能执行的,当我们将需要耗时的代码块写在不需要耗时的代码块前时,会产生阻塞,为了避免这种情况的发生JavaScript背后有一套属于自己的执行机制。

事件循环Event Loop

在介绍Event Loop之前,首先我们需要知道什么是异步什么是同步。

同步任务:

JavaScript 中,同步操作是指代码按照书写的顺序依次执行,每一行代码必须等待前一行代码执行完成后才能开始执行。这就像一个排队的过程,一个任务完成后才会开始下一个任务。同步操作会阻塞后续代码的执行,直到当前操作完成

异步任务:

异步操作是指代码不会按照书写顺序立即执行,而是在某个特定的事件发生或者某个条件满足后才会执行。这使得 JavaScript 能够在执行一个长时间运行的任务(如网络请求、定时器等)时,不会阻塞其他代码的执行。异步操作通常涉及到回调函数、Promiseasync/await等机制。

console.log(111);
setTimeout(() => {
  console.log(222);
}, 1000)
console.log(333);

image.png

如上图所示setTimeout是一个十分经典的异步任务,需要一定的时间去执行。

Event Loop

Event Loop(事件循环)是 JavaScript 的一种运行机制,用于处理异步操作。在 JavaScript 中,代码的执行是单线程的,这意味着同一时间只能执行一个任务。但是,实际的应用场景中有很多异步操作,如网络请求、定时器、用户交互事件等。Event Loop 的作用就是协调这些异步操作的执行顺序,使得 JavaScript 能够高效地处理多个任务而不会阻塞主线程。接下来需要了解任务分为以下两种任务的。

宏任务与微任务

如上上述,js的代码分成了同步代码与异步代码,而在这基础之上异步代码可以被理解为一个任务,任务又被分成了两大类分别是宏任务与微任务。

任务任务类型
JavaScript宏任务
事件宏任务
AJAX宏任务
readFile读取文件宏任务
promise微任务
async/await微任务

image.png

Promise

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。

const promise = new Promise(function (resolve, reject) { });

Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolvereject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。

resolve与reject

resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。这两个函数都是可选的,不一定要提供。它们都接受Promise对象传出的值作为参数。

then方法是promise原型上的一个函数,x.then() then函数会在x这个promise实例对象状态变更为resolved之后才执行内部逻辑,由此借助这个机制可以将异步捋成同步

例子:

接下来举一个简单的例子,让我们看看promise到底是如何进行使用的

function home() {
  setTimeout(() => {
    console.log('回家了');

  })
}

function eat() {
  setTimeout(() => {
    console.log('吃饭');
  }, 1000)
}

function sleep() {
  console.log('睡觉');
}
home()
eat()
sleep()

如上代码所示,很明显,我们希望实现的功能是,先回家再吃饭最后睡觉,但正常执行最终得不到我们想要的结果,此时便可用上promise来对这些异步代码进行操作了。

image.png

function home() {//promise对象有三种状态status:分别为:pending resloved rejected
  return new Promise((resolve, reject) => {
    //默认为status=pending
    setTimeout(() => {
      console.log('回家了');
      //此时status变为resolve
      resolve()
    }, 2000)
  })
}

function eat() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('吃饭');
      resolve()
    }, 1000)
  })
}

function sleep() {
  console.log('睡觉');

}


//只有status变为resloved时,.then函数才会被调用
home().then(() => {
  eat().then(() => {
    sleep()
  })
})
//第二种写法:
home()
  .then(() => {
    return eat()
  })
  //如果.then函数没有主动返回一个promise对象,则主动返回由
  .then(() => {
    sleep()
  })

image.png

只有当status变为了resolve时,才会使得.then被接收。resolve()会使得status的状态变为resolve,而reject()则会使得status的状态变为reject,最后会被.then或.catch接收。