转载

ES6之Promise

作者: kim先生
来源: kimshareclub微信公众号

今天我们讲的是ES6中的Promise这个异步操作对象。在学习Promise之前我们首先要了解Javascript的一些有关异步操作、JS事件机制等方面的知识,这样才能更好的吸收今天所讲的内容。所以先从浏览器的进程讲起!

##浏览器进程

浏览器是以多进程运行的,而我们的JS引擎是浏览器渲染进程中的一个线程(单线程),所谓的单线程就是一次只能执行一个任务,如果有多个任务需要处理的话,那么就需要排队,等待上一个任务执行完毕才会继续往下执行。这样就会有个弊端,就是如果当前执行的任务很耗时,那么等待的任务就会长时间的等待被执行,会出现一种假死(卡死)的状态,用户体验很不好。下面来看看浏览器都有哪些进程:
这里写图片描述

关于浏览器进程间是怎么个相互配合工作的,这里就不再详细展开,如果大家感兴趣的话,麻烦先添加小编的公众号,后续更新会推送给您。根据上面的图就很清楚的知道,我们平时写的JS代码是由JS引擎线程去处理的,而JS引擎线程是一个单线程,所以一次只能执行一个任务,为了改变这种不友好的用户体验,这时就会用到异步这个操作。所谓的JS异步并不是交由JS引擎去完成的,而是交给浏览器的其他线程去完成。JS异步操作还会涉及到JS事件循环机制。

##JS事件循环机制

下图就是JS事件循环机制的一个执行流程:
这里写图片描述

所谓的JS事件循环机制其实可以这么理解,当JS引擎去执行JS代码的时候会从上至下按顺序执行,当遇到异步任务的,就会交由浏览器的其他线程去执行,如果是setTimeout/setInterval定时异步任务,浏览器的渲染进程就会开一个定时器触发线程去执行,当定时时间一到,就会通知事件触发线程将定时器的回调方法推送至事件任务队列的一个宏任务队列的列尾,等待JS引擎执行完同步任务后,再从事件任务队列中从头取出要执行的回调方法。其他异步任务也是这么一个流程。这就是所谓的JS事件循环。这其中还涉及到了宏任务和微任务的一个概念,那什么是宏任务?什么是微任务呢?

##宏任务与微任务

下面的图会很清楚的告诉大家什么是宏任务及微任务:
这里写图片描述

我们发现今天要讲的Promise其实就是一个异步的微任务。宏任务与微任务的一个执行过程是这样的:
这里写图片描述

当JS引擎从任务队列中取出一个宏任务来执行,如果执行过程中有遇到微任务,那么执行完该宏任务就会去执行宏任务内的所有微任务。然后更新UI。后面就是再从任务队列中取出下一个宏任务来继续执行,以此类推。这是ES6的常考的一个知识点,希望大家一举拿下。

到这里或许大家会有些迷,不过不要紧,多看几次,或者去查些资料,争取拿下它。前面讲了那么多主要是将下面要讲的Promise贯穿起来,形成一个知识体系。而不是片面的了解它,或者只了解一部分,搞不清楚它是怎么串起来的。这也是我在百度上发现的一个痛点,很多知识点你搜的时候大部分文章讲的只有一部分,而且不同的文章得出的结论还不一样,所以我经过查阅大量的资料才总结出上面的这个知识点,目的只有一个,就是要把要学的东西弄清楚,知根知底那才是真的学到。

上面所讲的知识点可以总结为:浏览器的运行是靠几个进程相互配合工作的,JS引擎是浏览器渲染进程中的一个线程,它属于单线程,一次只能执行一个任务,如果有多任务则需要排队等待被执行,如需要执行异步操作就需要借助浏览器的其他线程完成,异步执行完成后将回调函数推送至任务队列中,待同步任务执行完毕后,再从任务队列中取出异步回调方法执行。任务队列中还可分为宏任务和微任务,如果宏任务中有微任务,那么当执行完宏任务,就立即执行微任务,然后才是更新UI。

##Promise

####Promise简介

Promise 是异步编程的一种解决方案,比传统的解决方案 (回调函数和事件)更合理和更强大。我们可以简单的把它理解为一个容器,它里面装的是一个异步操作(某个未来才会结束的事件)的结果。Promise操作后返回的对象还是一个新的Promise对象,所以支持链式调用,它可以把异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数,更便于理解与阅读。

Promise主要有以下特点:

1、Promise对象状态不受外界影响,它有三种状态:

pending:进行中

fulfilled:已成功

rejected:已失败

只有异步操作的结果才能确定当前处于哪种状态,任何其他操作都不能改变这个状态。这也是Promise(承诺)的由来。

2、Promise状态一旦改变就不会再变,任何时候都可以得到这个结果。它的状态改变只有两种结果:

2.1、从pending状态变为fulfilled状态

2.2、从pending状态变为rejected状态

只要有其中一种情况发生,状态就凝固了,不会再变,会一直得到这个结果,后续再添加Promise的回调函数也只能拿到前面状态凝固的结果

Promise缺点:

1、无法取消Promise,一旦新建它就会立即执行,无法中途取消

2、如果不设置回调函数(没有捕获错误),Promise内部抛出的错误,不会反应到外部

3、当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)

讲解Promise还会贯穿异步、同步、串行、并行等这几个慨念:

**异步:**多个任务可以同时执行,各不影响。

**同步:**多个任务必须排队,等待一个一个的执行。

**串行:**任务按顺序执行,前一个任务的结果可以是下个任务的参数,所以必须等待前一个任务执行完毕才能往下继续。

**并行:**多个任务可以同步执行,各不相关,实际就是一个异步操作的过程。

####Promise API

先来看看Promise都有哪些API:
这里写图片描述

由上图知,Promise既是一个对象也是一个构造函数,下面就具体分析它的每个api:

01、Promise.prototype.constructor()

它的基本用法如下:
这里写图片描述

Promise接收一个函数作为参数,函数里有resolve和reject两个参数,这两个参数其实是Promise内置的两个方法,会在异步操作执行结束后调用,可以将异步操作的结果回传至回调函数,以确定Promise最终的一个状态(是fulfilled还是rejected)。

resolve方法的作用是将Promise的pending状态变为fulfilled,在异步操作成功之后调用,可以将异步返回的结果作为参数传递出去。

reject方法的作用是将Promise的pending状态变为rejected,在异步操作失败之后调用,可以将异步返回的结果作为参数传递出去。

他们之间只能有一个被执行,不会同时被执行,因为Promise只能保持一种状态。

02、Promise.prototype.then()

Promise实例确定后,可以用then方法分别指定fulfilled状态和rejected状态的回调函数。它的基本用法如下:
这里写图片描述

then(onfulfilled,onrejected)方法中有两个参数,两个参数都是函数,第一个参数执行的是resolve()方法(即异步成功后的回调方法),第二参数执行的是reject()方法(即异步失败后的回调方法)(第二个参数可选)。它返回的是一个新的Promise对象。

一般推荐使用第二种写法,第二种写法能达到第一种写法的效果,但是二种写法还可以捕获onfulfilled执行过程中抛出的错,而第一种写法的onrejected方法无法捕获onfulfilled中抛出的错,具体代码如下:
这里写图片描述
这里写图片描述

如果Promise内部抛出的错不被捕获是不会反应到外部的:
这里写图片描述

then还可以很好的解决异步串行的操作,避免了传统异步串行操作层层嵌套的问题:
这里写图片描述
这里写图片描述

异步串行操作,主要思想是使各异步操作按一定顺序来执行,可以使用前一个异步操作的结果作为后一个异步的参数,严格确保前一个异步执行完毕后,再执行下一个异步

03、Promise.prototype.catch()

catch方法是.then(null,onrejected)的别名,用于指定发生错误时的回调函数。作用和then中的onrejected一样,不过它还可以捕获onfulfilled抛出的错,这是onrejected所无法做到的:
这里写图片描述
这里写图片描述

它的多种写法如下:
这里写图片描述

Promise错误具有"冒泡"的性质,如果不被捕获会一直往外抛,直到被捕获为止:
这里写图片描述

他们都只能捕获前面Promise抛出的错,而无法捕获在他们后面的Promise抛出的错:
这里写图片描述

04、Promise.prototype.finally()

finally方法用于指定不管Promise对象最后状态如何,都会执行的操作。该方法是 ES2018 引入的标准,用法类似于Java中的finally:
这里写图片描述

finally方法不接受任何参数,故可知它跟Promise的状态无关,不依赖于Promise的执行结果。

05、Promise.all()

Promise.all方法接受一个数组作为参数,但每个参数必须是一个Promise实例。Promise的all方法提供了并行执行异步操作的能力,并且在所有异步操作都执行完毕后才执行回调,只要其中一个异步操作返回的状态为rejected那么Promise.all()返回的Promise即为rejected状态,此时第一个被reject的实例的返回值,会传递给Promise.all的回调函数:
这里写图片描述

如果Promise.all的Promise实例参数自己定义了catch方法,那么它一旦被rejected,就不会触发Promise.all()的catch方法了,而是执行了then中的方法:
这里写图片描述

如果Promise.all的Promise实例参数自己没有定义catch方法,那么它一旦被rejected,就会触发Promise.all()的catch方法:
这里写图片描述

06、Promise.race()

Promise的race方法和all方法类似,都提供了并行执行异步操作的能力,但是all方法的效果实际上是「谁跑的慢,以谁为准执行回调」,而race方法则是「谁跑的快,以谁为准执行回调」,以下就是race的执行过程:
这里写图片描述

07、Promise.resolve()

resolve方法可以将现有的对象转换为Promise对象。该实例的状态为fulilled。resolve方法的参数除了正常值之外还可以传Promise实例。传正常值的情况如下:
这里写图片描述

当resolve函数传进去的参数是一个Promise实例情况如下:
这里写图片描述

08、Promise.reject()

reject方法也会返回一个新的 Promise 实例,该实例的状态为rejected。
这里写图片描述

##Promise相关应用

1、Promise一旦创建就会立即执行,中途无法取消,JS中的其他对象一般是调用之后才会执行。代码执行流程如下:
这里写图片描述

从上面的代码执行流程来看,new Promise()对象属于宏任务,一旦创建就会执行,而Promise.resolve()才是属于微任务。配合前面讲的任务执行流程来看,可知:new Promise()和console.log()属于script(全局宏任务),JS代码是从上至下执行的,所以先执行全局宏任务,当遇到Promise.resolve()这个微任务,待全局宏任务执行完就会立即执行微任务,当执行完微任务就会进行一次UI更新,然后再到任务队列中取出下个宏任务(setTimeout()),依次执行。所以得出了以上的打印结果。

2、如果不设置回调函数,Promise内部抛出的错误,不会反应到外部:
这里写图片描述

3、Promise状态一旦确定,就不会再变,任何时候都可以得到这个结果:
这里写图片描述

上面的代码,在resolve后面抛出错误,不会被捕获,等于没有抛出。

4、调用resolve或reject方法并不会终结其后面函数的执行:
这里写图片描述

5、Promise.all与Promise.race的应用对比:
这里写图片描述
这里写图片描述
这里写图片描述

##总结

再次严重声明,以上内容均是自己边学习边总结下来的知识点,由于个人能力有限,如有不对之处,欢迎留言纠正指出。以上精华来之不易,如果大家觉得有帮助,或者觉得还行的话,麻烦帮忙点赞关注下!让我看到你们双手,我先谢为敬!!!

###扫码关注
这里写图片描述

正文到此结束
本文目录