前言:本文主要是学习Promises相关用法和实现原理,通过对promise的实现来了解起原理,加强理解。
1 JS的同步和异步
- 首先,对于js来说,js语言的运行环境是单线程的,也就是说一次只能完成一个任务,也就是只存在一条流水线,如果有多个任务需要进行就必须排队,等待前面一个任务完成再进行下一个任务。
1.1什么是同步和异步?
为什么要单线程?
JavaScript
的单线程与他的用途有关。作为浏览器脚本语言,JS的主要用途是和用户互动,以及操作DOM。这就决定了它只能是单线程,否则就会带来很多复杂的同步问题。比如说,假定JS同时有两个线程,一个线程在某个DOM节点添加内容,另一个线程删除这个节点,这是就会出现矛盾,浏览器应该以哪个线程为准?
所以为了避免复杂性,从一诞生,
JS
就是单线程。为了利用多核CPU
的计算能力,HTML5
提出Web Worker
标准,允许JavaScript
脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM
。所以,这个新标准并没有改变JavaScript
单线程的本质。同步和异步的定义:
JavaScript
异步表示async
,指:代码执行不按顺序,跳过
执行,待其他某些代码执行完后,再来执行,称为异步
。JavaScript同步表示
sync
,指:代码依次执行,称为同步
。简单的理解:
- 可以改变程序执行顺序的操作就可以看成为异步操作。其实异步和同步的差别很简单,就在于这条流水线上各个流程的执行顺序不同。
异步的运行机制:
(1) 所有的任务都在主线程上执行,形成一个执行栈
Execution context stack
(2) 主线程之外,还存在一个任务队列
Task queue
。只要异步任务有了结果,就在任务队列之中放置一个事件。(3) 一旦执行栈中的所有同步任务执行完毕,系统就会读取任务队列,看看里面有哪些事件,事件对应异步任务,于是结束等待状态,进入执行栈,开始执行任务队列中的任务
(4) 主线程不断重复上面第三步,只要主线程空了,就会读取任务队列,这就是JavaScript的运行机制,这个过程会不断重复。
1.2 同步异步实际体验
执行顺序为:从中可以看出,就算事件设置0ms,也会先执行主线程上的任务,即1和5;setTimeout 的函数为异步任务,不进入主线程,而进入任务队列。程序先执行同步里的内容,执行完毕后,任务队列开始通知主线程,请求执行任务,该任务才会进入主线程来执行。
2 Promise的用法
作用:利用
promise
可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,promise
对象提供统一的接口,使得控制异步操作更加容易。
2.1 Promise的三个状态
Pending
:Promise的初始状态,等到任务完成或者被拒绝;Resolved
:执行完成并且成功的状态;Rejected
:执行完成并且失败的状态。
2.2 Promise对象必须实现then方法
- then是promise的核心,而且then方法必须返回一个promise对象,同一个promise对象可以注册多个then方法,并且回调的执行顺序和他们注册的顺序一致。
- then方法接收两个回调函数,他们分别是成功时的回调和失败时的回调:
- value值表示的是异步执行成功之后在promise函数中获取的值,不确切的说就是可以获取该函数的私有变量,将promise的值获取过来之后在then中可以实现值的相应应用。
- 其中,value值表示的是异步执行成功之后在promise函数中获取的值,不确切的说就是可以获取该函数的私有变量,将promise的值获取过来之后在then中可以实现值的相应应用。
3 那如何理解Promise?
举个具体的例子吧,某天,突发奇想,发了封邮件给木匠师傅,定制一个如此这般的家具。
木匠有求必应,即是说,邮件一旦发出就得到了他的承诺(Promise):在下一定尽力。
邮件中规定好了结果的通知方式:
成功了,直接将家具(res)邮递(resolve)过来。
失败了,直接将失败的信息(err)发邮件(reject)过来。
邮件发出等价于得到木匠的承诺P,之后,能做的只有等待(then)。
3.1 Promise 的行为特征
3.1.1 状态
每个Promise有三种状态:进行中(
pending
)、已成功(resolved
)和已失败(rejected
)。一旦变成结果状态,即更改成resolved/rejected,状态便被冷冻,不能再被更改。
状态容器
- Promise实质是个状态容器。 得到结果状态后,任何时候都可以访问到此状态。这与事件订阅通知不同,如果订阅发生在通知之后,订阅是不起作用的。
状态不可控
- 一旦创建Promise,便会立刻执行,无法取消。处于pending状态时,无法得知进程具体的信息,比如完成百分比(虽然可以自行设置回调进行通知)。
失败的状态
- 成功的状态只能由
resolve
方法转成。 失败的状态可以由reject
方法转成,也可以由抛出错误间接转成。三者都会正常的打印出失败的信息。
错误的报告机制
如果失败状态没有接收失败的回调函数接收,Promise会抛出错误。 这里的抛出错误,仅仅是在控制台显示之类的提示,不会终止程序的进程。
3.1.2
传入方法
创建Promise的同时也会执行传入方法。
传入方法不会因为调用了resolve/reject便终止执行,所以更优的方式是retrun resolve/reject。
回调方法
立即得到结果的Promise,其回调函数依然会晚于本轮事件执行。
这种后执行不同于setTimeout的将执行函数push到执行栈,而是将执行函数放到本轮的末尾。
结果参数
传入reject的参数,一般是字符串或Error实例,表示抛出的错误。
传入resolve的参数,一般是相应的JSON数据等,表示得到的数据。
传入resolve的参数,还可以是另一个Promise实例。
这时,只有当内层的Promise结束后,外层的Promise才会结束。
总结
Promise 是针对js中的异步问题提供的一种数据容器,为后续的知识学习做了铺垫。