我们知道 ES6 出现之后,事件循环机制和之前的就有些不同,这篇文章会讲这些不同的地方讲清楚。
我们先从浏览器的进程讲起!
浏览器进程
下面来看看浏览器都有哪些进程:
根据上面的图就很清楚的知道,JS 引擎线程是一个单线程,所以一次只能执行一个任务,为了改变这种不友好的用户体验,这时就会用到异步这个操作。所谓的 JS 异步并不是交由 JS 引擎去完成的,而是交给浏览器的其他线程去完成。JS 异步操作还会涉及到 JS 事件循环机制。
JS事件循环机制
下图就是JS事件循环机制的一个执行流程:
所谓的 JS 事件循环机制其实可以这么理解,当 JS 引擎去执行 JS 代码的时候会从上至下按顺序执行,当遇到异步任务的,就会交由浏览器的其他线程去执行,如果是setTimeout/setInterval 定时异步任务,浏览器的渲染进程就会开一个定时器触发线程去执行,当定时时间一到,就会通知事件触发线程将定时器的回调方法推送至事件任务队列的一个宏任务队列的列尾,等待 JS 引擎执行完同步任务后,再从事件任务队列中从头取出要执行的回调方法。其他异步任务也是这么一个流程。这就是所谓的 JS 事件循环。
那什么是宏任务?什么是微任务呢?
宏任务与微任务
下面的图会很清楚的告诉大家什么是宏任务及微任务:
我们发现今天要讲的 Promise 其实就是一个异步的微任务。宏任务与微任务的一个执行过程是这样的:
当JS引擎从任务队列中取出一个宏任务来执行,如果执行过程中有遇到微任务,那么执行完该宏任务就会去执行宏任务内的所有微任务。然后更新UI。后面就是再从任务队列中取出下一个宏任务来继续执行,以此类推。
Promise
Promise简介
Promise 是异步编程的一种解决方案,比传统的解决方案 (回调函数和事件)更合理和更强大。我们可以简单的把它理解为一个容器,它里面装的是一个异步操作(某个未来才会结束的事件)的结果。
Promise 操作后返回的对象还是一个新的 Promise 对象。
Promise主要有以下特点:
1
Promise对象状态不受外界影响,它有三种状态:
pending:进行中
fulfilled:已成功
rejected:已失败
只有异步操作的结果才能确定当前处于哪种状态,任何其他操作都不能改变这个状态。这也是Promise(承诺)的由来。
2
Promise状态一旦改变就不会再变,任何时候都可以得到这个结果。它的状态改变只有两种结果:
1、从pending状态变为fulfilled状态
2、从pending状态变为rejected状态
只要有其中一种情况发生,状态就凝固了,不会再变,会一直得到这个结果,后续再添加Promise的回调函数也只能拿到前面状态凝固的结果
Promise缺点:
1
无法取消Promise,一旦新建它就会立即执行,无法中途取消
2
如果不设置回调函数(没有捕获错误),Promise内部抛出的错误,不会反应到外部
3
当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)
Promise API
先来看看Promise都有哪些API:
由上图知,Promise既是一个对象也是一个构造函数,下面就具体分析它的 api:
01
Promise.prototype.constructor()
它的基本用法如下:
Promise接收一个函数作为参数,函数里有resolve和reject两个参数:
1. resolve方法的作用是将Promise的pending状态变为fulfilled,在异步操作成功之后调用,可以将异步返回的结果作为参数传递出去。
2. reject方法的作用是将Promise的pending状态变为rejected,在异步操作失败之后调用,可以将异步返回的结果作为参数传递出去。
他们之间只能有一个被执行,不会同时被执行,因为Promise只能保持一种状态。
02
Promise.prototype.then()
Promise实例确定后,可以用then方法分别指定fulfilled状态和rejected状态的回调函数。它的基本用法如下:
then(onfulfilled,onrejected)方法中有两个参数,两个参数都是函数,第一个参数执行的是resolve()方法(即异步成功后的回调方法),第二参数执行的是reject()方法(即异步失败后的回调方法)(第二个参数可选)。它返回的是一个新的Promise对象。
03
Promise.prototype.catch()
catch方法是.then(null,onrejected)的别名,用于指定发生错误时的回调函数。作用和then中的onrejected一样,不过它还可以捕获onfulfilled抛出的错,这是onrejected所无法做到的:
Promise错误具有"冒泡"的性质,如果不被捕获会一直往外抛,直到被捕获为止;而无法捕获在他们后面的Promise抛出的错。
04
Promise.prototype.finally()
finally方法用于指定不管Promise对象最后状态如何,都会执行的操作。该方法是 ES2018 引入的标准:
finally方法不接受任何参数,故可知它跟Promise的状态无关,不依赖于Promise的执行结果。
05
Promise.all()
Promise.all方法接受一个数组作为参数,但每个参数必须是一个Promise实例。Promise的all方法提供了并行执行异步操作的能力,并且在所有异步操作都执行完毕后才执行回调,只要其中一个异步操作返回的状态为rejected那么Promise.all()返回的Promise即为rejected状态,此时第一个被reject的实例的返回值,会传递给Promise.all的回调函数:
06
Promise.race()
Promise的race方法和all方法类似,都提供了并行执行异步操作的能力,但是all方法的效果实际上是「谁跑的慢,以谁为准执行回调」,而race方法则是「谁跑的快,以谁为准执行回调」,以下就是race的执行过程:
最后的一个面试题
各位知道这代码在浏览器的执行结果吗:
console.log('1');
setTimeout(function() { console.log('2'); process.nextTick(function() { console.log('3'); }) new Promise(function(resolve) { console.log('4'); resolve(); }).then(function() { console.log('5') })})process.nextTick(function() { console.log('6');})new Promise(function(resolve) { console.log('7'); resolve();}).then(function() { console.log('8')})
setTimeout(function() { console.log('9'); process.nextTick(function() { console.log('10'); }) new Promise(function(resolve) { console.log('11'); resolve(); }).then(function() { console.log('12') })})
最近总有朋友问我 Vue 相关的问题,因此接下来我会输出 10 篇 Vue 相关的文章,希望对大家有一定的帮助。我会保持在 7 到 10 天更新一篇。