期约(Promise)
期约的状态代表期约是否完成,有三种状态:
-
待定(
pending
): 表示尚未开始或者正在执行中。 -
兑现(
fulfilled
,有时候也称为“解决”,resolved
): 表示已经成功完成。 -
拒绝(
rejected
): 则表示没有成功完成,也就是已经失败。
在待定状态下,期约可以落定(settled
)为代表成功的兑现(fulfilled
)状态,或者代表失败的拒绝(rejected
)状态。每个期约只要状态切换为兑现(fulfilled
),就会有一个私有的内部值(value
)。类似地,每个期约只要状态切换为拒绝(rejected
),就会有一个私有的内部理由(reason
)。无论是值还是理由,都是包含原始值或对象的不可修改的引用。二者都是可选的,而且默认值为undefined
。在期约到达某个落定(settled
)状态时执行的异步代码始终会收到这个值(value
)或理由(reason
)。
Promise.resolve()
通过调用
Promise.resolve()
静态方法,可以实例化一个解决的期约。
//下面两个期约实例实际上是一样的:
let p1 = new Promise((resolve, reject) => resolve());
let p2 = Promise.resolve();
这个解决的期约的值(value
)对应着传给Promise.reslove()的第一个参数。使用这个静态方法,实际上可以把任何值都转换为一个期约:
setTimeout(console.log, 0, Promise.resolve()); //Promise {<resolved>: undefined}
setTimeout(console.log, 0, Promise.resolve(3)); //Promise {<resolved>: 3}
//多余的参数会忽略
setTimeout(console.log, 0, Promise.resolve(3, 4, 5)); //Promise {<resolved>: 3}
Promise.resolve()可以说是一个幂等方法。所谓的幂等是指多个请求返回相同的结果。用数学表单的话: X^Y = X
let p = Promise.resolve(6);
setTimeout( console.log, 0, p === Promise.resolve(p)); // true
setTimeout( console.log, 0, p === Promise.resolve(Promise.resolve(p))); // true
Promise.reject()
与
Promise.resolve()
类似,Promise.reject()
会实例化一个拒绝的期约(Promise
)并抛出一个异步错误(这个错误不能通过try/catch捕获。而只能通过拒绝处理程序捕获)。
//下面两个期约实例实际上是一样的:
let p1 = new Promise((resolve, reject) => reject());
let p2 = Promise.reject();
这个拒绝的期约的理由就是传给Promise.reject()
的第一个参数。这个参数也会传给后续的拒绝处理程序:
let p = Promise.reject(3);
setTimeout(console.log, 0, p); //Promise {<rejected>: 3}
p.then(null, (e) => setTimeout(console.log, 0, e)); //3
关键在于,Promise.reject()
并没有照搬Promise.resolve()
的幂等逻辑。如果给它传一个期约(Promise
)对象,则这个期约会成为它返回的拒绝(rejected
)期约的理由:
setTimeout(console.log, 0, Promise.reject(Promise.resolve()));
//Uncaught (in promise)
//Promise {<resolved>: undefined}
//Promise {<rejected>: Promise}
Promise.prototype.then()
这个then()
方法接收最多两个参数: onResolved
处理程序和onRejected
处理程序。这两个参数都是可选的,如果提供的话,则会在期约分别进入"兑现"和"拒绝"状态时执行。
上面用到了Promise.prototype.then()
方法,该方法返回一个新的期约实例。
let p1 = new Promise(() => {});
let p2 = p1.then();
setTimeout(console.log, 0, p1 === p2); // false
- 这个新的期约实例基于
onResolved
处理程序的情况如下:
在then()
方法的回调中没有传入返回值的话,也就是如果没有显式的返回语句,则Promise.resolve()
会包装默认的返回值:undefined。
let p1 = Promise.resolve('foo');
let p2 = p1.then(() => {});
let p3 = p1.then(( => Promise.resolve());
setTimeout(console.log, 0, p2); // Promise {<resolved>: undefined}
setTimeout(console.log, 0, p3); // Promise {<resolved>: undefined}
如果有显式的返回值,则Promise.resolve()会包装这个值。
let p4 = p1.then(() => 'bar');
let p5 = p1.then(( => Promise.resolve('bar'));
setTimeout(console.log, 0, p4); // Promise {<resolved>: "bar"}
setTimeout(console.log, 0, p5); // Promise {<resolved>: "bar"}
抛出异常会返回拒绝的期约:
let p6 = p1.then(() => {throw 'baz';}); // Uncaught (in promise) baz
setTimeout(console.log, 0, p6); //Promise {<rejected>: "baz"}
注意:返回错误值不会触发上面的拒绝行为,而会把错误对象包装在一个解决的期约中:
let p7 = p1.then(() => Error('err'));
setTimeout(console.log, 0, p7); //Promise {<resolved>: Error: err
at <anonymous>:1:24}
- 在基于
onRejected
处理程序时: 跟onResolved
的类似。
onRejected
处理程序返回的值也会被Promise.resolve()
包装。在抛出异常或传入返回Promise.reject()
时才会返回拒绝的期约。
let p1 = Promise.reject('foo');
let p2 = P1.then(null, () => {}); //Promise {<resolved>: undefined}
let p3 = p1.then(null, () => Promise.resolve()); //Promise {<resolved>: undefined}
let p4 = p1.then(null, () => Promise.reject()); //Promise {<rejected>: undefined}
let p5 = p1.then(null, () => {throw 'baz';}); // Promise {<rejected>: "baz"}
let p6 = p1.then(null, () => Error('qux')); // Promise {<resolved>: Error: qux at <anonymous>:1:26}
Promise.prototype.catch()
待续。
Promise的应用场景:
可以解决多请求,合并请求处理错误,多验证的场景。
Tips:
同步是有序的进入内存运行
。
而期约的异步形式是期约的处理程序是按照它们添加的顺序执行的,是先添加到“消息队列”
。