本文原创:duxiaoxuehtml
一旦一个 Promise 决议,不管是如今仍是未来,下一个步骤老是相同的。git
既然 Promise 是经过
new Promise(..)
语法建立的,那你可能就认为能够经过p instanceof Promise
来检查。但遗憾的是,这并不足以做为检查方法。识别 Promise(或者行为相似于 Promise 的东西)就是定义某种称为 thenable 的东西,将其定义为任何具备then(..)
方法的对象和函数。咱们认为,任何这样的值就是 Promise 一致的 thenable。es6Promise 模式构建的可能最重要的特性:信任。github
即便是当即完成的 Promise(相似于
new Promise(function(resolve){ resolve(42); })
)也没法被同步观察到。也就是说,对一个 Promise 调用then(..)
的时候,即便这个 Promise 已经决议,提供给then(..)
的回调也总会被异步调用。编程一个 Promise 决议后,这个 Promise 上全部的经过
then(..)
注册的回调都会在下一个异步时机点上依次被当即调用。这些回调中的任意一个都没法影响或延误对其余回调的调用。promise-- YDKJS安全
所谓"异步",简单说就是一个任务不是连续完成的,能够理解成该任务被人为分红两段,先执行第一段,而后转而执行其余任务,等作好了准备,再回过头执行第二段。 ... 相应地,连续的执行就叫作同步。因为是连续执行,不能插入其余任务,因此操做系统从硬盘读取文件的这段时间,程序只能干等着。异步
--阮一峰《ES6入门》异步编程
setTimeout(() => {
// statements
}, 1000)
复制代码
var xhr = new XMLHttpRequest(),
method = "GET",
url = "https://developer.mozilla.org/";
xhr.open(method, url, true);
xhr.onreadystatechange = function () {
if(xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
console.log(xhr.responseText);
}
};
xhr.send();
复制代码
缺点:函数
大脑对于事情的计划方式是线性的、阻塞的、单线程的语义,可是回调表达异步流程的方式是非线性的、非顺序的,这使得正确推导这样的代码难度很大。 回调函数的调用控制交与第三方函数内部,没法保证回调函数必定会被正确调用。可能出现不少异常状况:所需参数传递错误、调用回调过早或过晚、调用回调次数太多或太少、吞掉可能出现的错误与异常等等。 回调函数是 JavaScript 异步的基本单元。可是随着 JavaScript 愈来愈成熟,对于异步编程领域的发展,回调已经不够用了。 ... 咱们须要一种更同步、更顺序、更阻塞的的方式来表达异步,就像咱们的大脑同样。 也须要一个通用方案来解决信任问题。
-- YDKJS
若是咱们不把本身程序的 continuation 传给第三方,而是但愿第三方给咱们提供了解其任务什么时候结束的能力,而后由咱们本身的代码来决定下一步作什么。这种范式就称为
Promise
。
Promise
决议后就是外部不可变的值,咱们能够安全地把这个值传递给第三方,并确信它不会被有意无心地修改。特别是对于多方查看同一个 Promise 决议的状况,尤为如此。一方不可能影响另外一方对 Promise 决议的观察结果。 不可变性听起来彷佛一个学术话题,但实际上这是Promise
设计中最基础和最重要的因素。
Promise
是一种封装和组合将来值的易于复用的机制。-- YKDJS
最先的Promise是由社区首先提出和实现的,早期比较有名的有jQuery的Deferred对象、bluebird、Q等等。ES6也将Promise归入语言标准,提供了原生的Promise对象。
最新的规范是在2014年发布的promise/A+。
一个promise
指示一个异步操做的最终结果。与promise
交互的主要方法是经过它的then
方法,在then
方法中注册了接收promise
的最终值或是没法被完成缘由的回调。
Promise/A+只专一于提供可操做的then
方法的规范。
规范中指出,ECMAScript语言规范中的Promise对象基于本规范还实现了许多额外的要求,也就是说咱们能够自行实现一个彻底遵照promise/A+规范但没必要彻底遵照ECMAScript语言规范的Promise,某种程度上说ES6里面的Promise也只是许多promise/A+规范实现的一种。
promise
: 带有按规范实现的then
方法的对象或函数thenable
: 定义了then
方法的对象或函数value
: 任何合法的JavaScript
值(包括undefined
、thenable
或是promise
),终值exception
: 使用throw
抛出的值reason
: 指示promise
被拒缘由的值,拒因(一). Promise
的状态
一个`promise`必须是这三种状态之一:
- `pending`(进行中、等待中)
- `fulfilled`(被完成、被执行)
- `rejected`(被拒绝)
状态的迁移:
1. 当`pending`时,`promise`的状态能够变到`fulfilled`或是`rejected`
2. 当`fulfilled`时,`promise`的状态不可再变,同时必须持有一个**不可变**的`value`(终值)
3. 当`rejected`时,`promise`的状态不可再变,同时必须持有一个**不可变**的`reason`(拒因)
这里的**不可变**指的是恒等(便可用 `===` 判断相等),但不意味着更深层次的不可变(如非基本类型值时,只要求引用地址相等)。
复制代码
(二). then
方法
一个`promise`必须提供一个`then`方法以访问其当前值、终值和据因。方法接受两个参数:
```js
promise.then(onFulfilled, onRejected)
```
1. `onFulfilled`、`onRejected`均为可选参数,若是不是函数类型,则必须被忽略
2. 若是`onFulfilled`是个函数:在promise被完成前不可被调用;在promise被完成后必须被调用,以promise的终值做为第一个参数;不能被屡次调用。
3. 若是`onRejected`是个函数:在promise被拒绝前不可被调用;在promise被拒绝后必须被调用,以promise的据因做为第一个参数;不能被屡次调用。
4. 调用时机
保证`onFulfilled`、`onRejected`在`then`被调用的那轮事件循环以后的新执行栈中异步执行。
这一点可使用宏任务(macro-task)机制如`setTimeout`、`setImmediate`,或微任务(micro-task)机制如`MutationObserver`、`process.nextTick`来实现。
5. 调用要求
`onFulfilled`和`onRejected`必须被做为函数调用(即没有 this 值)(在严格模式中,函数`this`的值为`undefined`;在非严格模式中其为全局对象)
6. `then`方法能够被同一个promise调用屡次
- 当promise被完成时,全部`onFulfilled`按注册顺序依次回调
- 当promise被拒绝时,全部`onRejected`按注册顺序依次回调
7. `then`方法必须返回一个promise对象
```js
promise2 = promise1.then(onFulfilled, onRejected);
```
- 若是`onFulfilled`、`onRejected`返回一个值x,则运行下面决议`promise`的过程:`[[Resolve]](promise2, x)`
- 若是`onFulfilled`、`onRejected`抛出一个异常e,则`promise2`必须拒绝执行,并返回拒因e
- 若是`onFulfilled`不是函数且`promise1`成功执行, `promise2`必须成功执行并返回相同的值
- 若是`onRejected`不是函数且`promise1`拒绝执行,`promise2`必须拒绝执行并返回相同的拒因
复制代码
(三). 决议promise
的过程(即[[Resolve]](promise, x)
的具体实现)
1. 若是`x`与`promise`相等,以`TypeError`为拒因拒绝执行promise
2. 若是`x`为`Promise`的实例,则使`promise`接受`x`的状态
- 若是`x`进行中,`promise`也需保持进行中的状态直至`x`被完成或拒绝
- 若是`x`被完成,用相同的值执行`promise`
- 若是`x`被拒绝,用相同的据因拒绝`promise`
3. 若是`x`为函数或者对象
1. 把`x.then`赋值给`then`
2. 若是取`x.then`的值时抛出错误`e` ,则以`e`为据因拒绝`promise`
1. 若是`then`是函数,将`x`做为函数的做用域`this`调用之。传递两个回调函数做为参数,第一个参数叫作`resolvePromise`,第二个参数叫作`rejectPromise`:
- 若是`resolvePromise`以值`y`为参数被调用,则运行`[[Resolve]](promise, y)`
- 若是`rejectPromise`以据因`r`为参数被调用,则以据因`r`拒绝`promise`
- 若是`resolvePromise`和`rejectPromise`均被调用,或者被同一参数调用了屡次,则优先采用首次调用并忽略剩下的调用
- 若是调用`then`方法抛出了异常`e`:
- 若是`resolvePromise`或`rejectPromise`已经被调用,则忽略之
- 不然以`e`为据因拒绝`promise`
2. 若是`then`不是函数,以`x`为参数执行`promise`
3. 若是`x`不为对象或者函数,以`x`为参数执行`promise`
复制代码
若是按照规范自行实现Promise,可使用下面官方提供的工具监测是否符合规范。
掘金上能够找到一些不错的练习题,用来巩固知识再好不过了。