Promise
为了解决 “回调地狱”(callback hell)而提出的写法
允许将callback
变成链式调用
它是一个容器,保存着某个异步操作的结果。
特点
对象的状态部受外界影响;有三种状态, 进行中( pending ) 、 成功( fulfilled ) 、 失败( rejected );只有异步操作的结果可以决定那种状态。
状态一旦改变,就不会更改(resolved 定型),状态的改变只会是两种情况 pengding -> fulfilled or pengding -> rejected;
, 中途无法取消,无法得知事件进展,不设置callback,内部将会抛出错误
简单实列
使用
Promise
构造函数生成实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//example
const promise = new Promise(function (resolve,reject) {
//... do something
$.get(url,function (data) {
if (data) {
resolve(data)
}
}).fail(function(err){
reject(err)
})
});
promise.then(function(data) {
console.log(data)
},function(err){
console.log(err)
})
可见 Promise
接受一个callback 作为参数, callback有两个参数,都是函数
- 第一个
resolve
异步操作成功时调用,将成功的信息作为参数传递出去 - 第二个
rejcet
异步操作失败时调用,将失败的信息传递出去
then 方法
生成实例后,可以使用 then
方法 接收成功状态和失败状态的回调,
- 第一个参数是成功后的callback (必传)
- 第二个参数是失败后的callback (为可选参数)
1 |
|
catch 方法
指定发生错误时的回调函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34p.then((val) => console.log('fulfilled:', val))
.catch((err) => console.log('rejected', err));
// 等同于
p.then((val) => console.log('fulfilled:', val))
.then(null, (err) => console.log("rejected:", err));
//状态改变后不会再接收之后的状态
const promise = new Promise(function(resolve, reject) {
resolve('ok');
reject( new Error('test'));
});
promise
.then(function(value) { console.log(value) }) //ok
.catch(function(error) { console.log(error) });
// promise 内部错误不会影响外部的代码,不会退出进程、终止脚本执行
const promise = new Promise(function(resolve, reject) {
resolve(Owen);
});
promise.catch(function(error){console.log(error)}).then(function(v){console.log('Owen:' + v)})
console.log(18)
//18
//error
//Owen:undefined
//如果将 then 放到第一个将不会执行
//因为 then 接收的是 promise变量 的状态
//而放到末尾则接收的是 catch 返回的 Promise 函数的状态finally
不管 promise 的状态怎么变都会执行的方法 (ES2018)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16//finally方法的回调函数不接受任何参数
promise.finally(() => {
// 语句
});
// 等同于
promise.then(
result => {
// 语句
return result;
},
error => {
// 语句
throw error;
}
);
all
- 用于将多个 Promise 实例,包装成一个新 Promise 实例
- 参数是一个数组或者具有 Interator的接口(返回成员必须是promise),如果成员不是 Promise 实例,会将其包装成一个porimse 实例
- 等待所以成员的状态都变成
resolved
状态,Promise.all
才变成resolved
状态,并且 - 数组成员中只有又一位成员为
rejectd
状态,Promise.all
就会变成rejected
, 并 - 如果成员自己定义了
then
或者catch
对应成员的返回值不会传递到all
的回调当中
1 | //example |
race
- 和
all
方法一样 只是成员状态发生改变的情况不同 - 数组中只要有一个成员改变状态,
race
就改变状态,并将返回值传递给race
方法
1 | //example |
resolve
- 将对象转化为Promise对象
1
2
3Promise.resolve('Owen')
// 等同
new Promise(resolve => resolve('Owen'))
传参情况
Promise实例
- 返回出入的实例
传入 thenable
对象
- 具有
then
方法的对象,将其转化为Promise
对象
1 | var obj = { |
不是对象或者没有 thenable
方法
- 返回一个新的Promise,状态为
resolved
1
2
3
4const p = Promise.resolve(18);
p.then(function(re){
console.log(re) //18
})
reject
- 返回一个新的Promise,状态为
rejectd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15const p = Promise.reject('出错了');
// 等同
const p = new Promise((resolve, reject) => reject('出错了'))
const thenable = {
then(resolve, reject) {
reject('出错了');
}
};
Promise.reject(thenable)
.catch(e => {
console.log(e === thenable) //true
})
结合 Generator 使用
1 |
|
Promise/A+ 规范
- 实现者为开发者提供开放的 Promise标准,实现可靠,可互操作的JavaScript承诺。
术语(Terminology)
promise
是一个对象或函数,其then
方法 的行为符合此规范thenable
定义then
方法的对象或函数value
是任何合法的JavaScript
值 (包括undefined
、thenable
和promise
)exception
是使用throw
语句抛出的值reason
是一个值(拒因),表明承诺被拒绝的原因(拒绝回调的值)
要求(Requirements)
Promise states
- Promise 只有三种状态:pending 、 fulfilled(resolved) 和 rejected 状态
- pending(等待状态):可以过渡到 resolved 或 rejected 状态
- fulfilled (执行状态):无法再改变状态,且只有一个无法改变的
value
- rejected (拒绝状态):无法再改变状态,必须有一个
reason
- 这里的 ‘无法再改变’ 意味着不可改变的身份 使用
===
判断value
或reason
then
方法(The then
method)
一个
Promise
必须提供一个then
方法来访问当前 或 最终value
或reason
此方法接收两个参数:onFulfilled
,onRejected
必须忽略其中不是函数是参数onFulfilled
:必须在promise
执行状态(fufilled
)结束后调用,其第一个参数为value
,只能调用一次onRejected
: 必须在promise
拒绝状态(rejected
)结束后调用, 其第一个参数为reason
,只能调用一次
onFulfilled or onRejected 在执行上下文 (作用域)堆栈仅包含平台代码之前不得调用
意味着调用onFulfilled
,onRejected
须在新一轮event loop
中执行, 可用macro-task
(setTimout,setInterval,I/O,UI rendering,script
主线程) 或micro-task
(Promise, Object.observe,process.nextTick,MutationObserver) 机制来实现- onFulfilled or onRejected 必须作为函数调用,在
use strict
下this 为 undefined
then
在同一个 promise 中可以链式调用,按照对应注册顺序一次回调then
返回一个Promise对象1
promise2 = promise1.then(onFulfilled, onRejected);
- 任一
onFulfilled
或onRejected
返回一个值x ,则执行Promise 解决过程
onFulfilled
或onRejected
抛出异常 e, 则promise2
必须拒绝执行,并返回reason
- 如果
onFulfilled
不是函数且 Promise1 为执行fulfilled
状态,那么 promise2 必须返回和 Promise1 相同的value
- 如果
onRejected
不是函数且 Promise1 为rejected
状态,那么 promise2 必须返回和 Promise1 相同的reason
Promise 解决过程 (The Promise Resolution Procedure)
- Promise 解决过程是一个抽象的操作,作为输入一个Promise,和一个值,用
[[Resolve]](Promise2,x)
表示;
如果x
有then
方法并且看上去像一个Promise,Promise 解决过程程序会尝试promise采用状态x
,否则用x
的value
执行Promise;
对thenables的这种处理使promise的实现进行互操作,只要它们暴露出一个遵循Promise/A+
规范 兼容then
方法即可。它还允许Promise/A+
规范来“吸收”与合理不符合标准的实现then方法。
运行 [Resolve] 需遵循以下步骤:
- 如果promise和
x
指向同一个对象,以TypeError
为理由拒绝执行promise
。 - 如果
x
是 promise,则接收其状态:
2.1 如果x
为pending
, 则 Promise 保持等待直至x
改变状态
2.2 如果x
为resolved
,则 用相同的value
执行 promise
2.3 如果x
为rejected
,则 用相同的reason
执行 promise - 如果
x
是 Object or Function:
3.1 把x.then
赋值给then
3.2 如果xthen
抛出异常 结果e
,promise 状态变为rejected
,reason
为e
3.3 如果then
是一个函数,将x
作为函数作用域this
调用,传递两个回调函数作为参数,第一个为resolvePromise
, 第二个参数为rejectPromise
3.4 如果then不是一个函数, 则以3.3.1 如果 `resolvePromise` 以值 `y`为参数被调用,以 `r`为 `reason` ,则运行 `[[Resolve]](promise, y)` 3.3.2 如果 `rejectPromise` 用`r`为参数调用 ,则以 `r`拒绝 promise 3.3.3 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用第一次调用并忽略剩下的调用 3.3.4 如果调用then抛出异常`e`,resolvePromise或rejectPromise已被调用,请忽略它;否则以 `e` 为`reason`(据因)拒绝 promise
x
为参数执行 promise- 如果
x
不为Object or Function,以x
为参数执行 promise
- 如果
如果一个 promise 被一个循环的 thenable 链中的对象解决,而
[[Resolve]](promise, thenable)
的递归性质又使得其被再次调用,根据上述的算法将会陷入无限递归之中。
算法虽不强制要求,但也鼓励施者检测这样的递归是否存在,若检测到存在则以一个可识别的TypeError
为reason
(据因)来拒绝 promise手写简版 Promise
- 任一
1 | // 常用变量大写 |