ECMAScript 6 笔记(17)- Promise (1)

原创 乘风逐月 随笔 ES6 137阅读 23 天前 举报

一、Promise 简介

Promise 是异步编程的一种解决方案,比传统的解决方案(回调函数和事件)更合理更强大。
Promise,简单来说是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上来说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

1.Promise 对象的特点

(1)对象的状态不受外界影响。Promise 对象代表一个异步操作,有三种状态:pending(进行中),fulfilled(已成功)和 rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是 Promise 这名字的由来,它的英文含义是“承诺”,表示其他手段无法改变。

(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise 对象的状态改变,只有两种可能:从 pending 变为 fulfilled 和从 pending 变为 rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对 Promise 对象添加回调函数,也会立即得到这个结果。这与事件的区别是:事件的特点是如果错过它,再去监听,是得不到这个结果的。

(3)Promise 对象,就可以将异步操作的流程表达出来,避免了层层嵌套的回调函数。提供了统一的接口,使得异步操作更加容易。

2.Promise 的缺点

(1)无法取消 Promise,一旦新建它就会立即执行,无法中途取消。
(2)如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。
(3)当处于 pending 状态时,无法得知目前进展到哪一阶段(刚刚开始还是即将完成)。
注:后文的 resolved 统一只指 fulfilled 状态,不包含 rejected 状态。

3.基本用法

ES6 规定,Promise 对象是一个构造函数,用来生成 Promise 实例。

Promise 构造函数接受一个函数作为参数,该函数的两个参数分别是 resolve 和 reject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。
a. resolve 函数的作用是,将 Promise 对象的状态从“未完成”变成“成功”(即从 pending 变为 resolve),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去。
b. reject 函数的作用是,将 Promise 对象的状态从“未完成”变为“失败”(即从 pending 变为 rejectd),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

二、Promise 实例方法

1.then()

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

(1)用法

上例中,timeout 方法返回一个 Promise 实例,表示一段时间以后才会发生的结果。经过 ms 毫秒后,Promise 实例的状态变为 resolved,就会触发 then 方法绑定的指定回调函数。then 指定了 resolved 状态时的回调函数,这个函数接收了 Promise 对象传出来的值作为参数即 'done'。
注:Promise 新建后会立即执行

上例中,Promise 新建后立即执行,所有首先输出 'Promise'。然后 then 方法指定的回调函数将在当前脚本所有同步任务执行完成时才会执行,所以 'resolved' 最后输出。

(2)then() 的返回值

then() 方法的返回值是一个新的 Promise 实例(注意不是原来的那个Promise实例)。因此可以采用链式写法,即 then 方法后面再调用另一个 then 方法。
采用链式的 then,可以指定一组按照次序调用的回调函数。这样前一个回调函数若返回一个 Promise 对象(即有异步操作),后一个回调函数就会等待该 Promise 对象的状态发生改变,才会被调用。

(3)示例

a. 用 Promise 对象实现 ajax 操作,例如:

上例中,getJSON是对 XMLHttpRequest 对象的封装,用于发出一个针对 JSON 数据的 HTTP 请求,并且返回一个Promise对象。 在getJSON 内部,resolve 和 reject 函数调用时,都带有参数,那么它们的参数会被传递给回调函数。
b. resolve 函数的参数除了正常值以外还可能是一个 Promise 实例。例如:

上例中,p1 是一个 Promise,3秒后变为 rejected。p2 的状态在1秒之后改变,resolve 方法返回的是 p1。由于 p2 返回的是另一个 Promise,导致 p2 自己的状态无效了,由 p1 的状态决定 p2 的状态。所以,后面的 then 语句都变成针对 p1 的了。又过了2秒,p1 变为 rejected,触发 catch 方法指定的回调函数。
c. 调用 resolve 或 reject 并不会终结 Promise 的参数函数的执行,例如:

上例中,调用 resolve(1) 以后,后面的 console.log(2) 还会执行,并且会首先打印出来。这是因为立即 resolved 的 Promise 是在本轮事件循环的末尾执行,总是晚于本轮循环的同步任务。
一般来说,调用 resolve 或 reject 以后,Promise 的使命就完成了,后继操作应该放到 then 方法里面,而不是直接写在 resolve 或 reject 后面。所以,最好在 resolve 或 reject 前面加上 return 语句,这样就不会有意外。

2.catch()

Promise 实例的 catch 方法是 .then(null,rejection) 的别名,用于指定发生错误时的回调函数。catch 方法返回的还是一个 Promise 对象,因此后面还可以接着调用 then 方法。

上例中,写法1和写法2是等价的,可以发现 reject 方法的作用等同于抛出错误。

(1)注意

a. 如果 Promise 状态已经变成 resolved,再抛出错误是无效的。

上例中, resolve 后面,抛出的错误不会被捕获。因为 Promise 的状态一旦改变,就永久保持该状态,不会再变了。
b. Promise 的对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止,即错误总会被下一个 catch 语句捕获。
c. 不要在 then 方法里面定义 reject 状态的回调函数(即 then 的第二个参数),总是使用 catch 方法。

d. 如果没有用 catch 方法指定错误处理的回调函数,Promise 对象抛出的错误不会传递到外层代码,不会有任何反应。

上例中,报错后还是会打印出 111,这说明 Promise 内部的错误不会影响到 Promise 外部的代码。
e. catch 方法中还可以抛出错误,但是要再用一个 catch 捕获,否则不会传递到外层。

评论 ( 1 )
最新评论