Promise的一些缺点
- 无法取消
Promise,一旦新建它就会立即执行,无法中途取消 - 如果不设置回调函数,
Promise内部抛出的错误,不会反应到外部 - 当处于
pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)
基本用法
//接收一个函数作为参数,参数包含 resolve和reject函数(由js引擎提供)const promise = new Promise(function(resolve, reject) {// ... some codeif (/* 异步操作成功 */){resolve(value); //value值是任意的,表示状态从 pending -> resolved} else {reject(new Error('error')); //error值也是任意的,表示状态从 pending -> rejected}});//当调用了resolve或者reject函数时,就会触发此 then 方法的调用//then有两个函数参数,第二个为可选//第一个参数的入参对应 resolve函数入参,第二个参数类似promise.then(function(value) {// success}, function(error) { //可选// failure});
Promise.prototype.then()
作用是为 Promise 实例添加状态改变时的回调函数。前面说过,then方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数
getJSON("/post/1.json").then(
post => getJSON(post.commentURL) //post是resolve函数的入参,及 resolve(post)
).then(
comments => console.log("resolved: ", comments),
err => console.log("rejected: ", err) //err是reject函数入参,及 reject(err)
);
详尽的一些参数还是得参考 you don’t know jsreject(..)简单地拒绝promise,但是resolve(..)既可以完成promise,也可以拒绝promise,这要看它被传入什么值。如果resolve(..)被传入一个立即的,非Promise,非thenable的值,那么这个promise将用这个值完成。
但如果resolve(..)被传入一个Promise或者thenable的值,那么这个值将被递归地展开,而且无论它最终解析结果/状态是什么,都将被promise采用
Promise.prototype.catch()
Promise.prototype.catch方法是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数。
getJSON('/posts.json').then(function(posts) {
// ...
}).catch(function(error) {
// 处理 getJSON 和 前一个回调函数运行时发生的错误
console.log('发生错误!', error);
});
下面三种方式写法等价
1、直接throw error
const promise = new Promise(function(resolve, reject) {
throw new Error('test');
});
promise.catch(function(error) {
console.log(error);
});
2、try-catch方式
const promise = new Promise(function(resolve, reject) {
try {
throw new Error('test');
} catch(e) {
reject(e);
}
});
promise.catch(function(error) {
console.log(error);
});
3、直接reject方式
const promise = new Promise(function(resolve, reject) {
reject(new Error('test'));
});
promise.catch(function(error) {
console.log(error);
});
一般来说,不要在then方法里面定义 Reject 状态的回调函数(即then的第二个参数),总是使用catch方法。
Promis“吃掉异常”
跟传统的try/catch代码块不同的是,如果没有使用catch方法指定错误处理的回调函数,Promise 对象抛出的错误不会传递到外层代码,即不会有任何反应
const someAsyncThing = function() {
return new Promise(function(resolve, reject) {
// 下面一行会报错,因为x没有声明
resolve(x + 2);
});
};
someAsyncThing().then(function() {
console.log('everything is great');
});
setTimeout(() => { console.log(123) }, 2000);
// Uncaught (in promise) ReferenceError: x is not defined
// 123
使用顺序建议
一般总是建议,Promise 对象后面要跟catch方法,这样可以处理 Promise 内部发生的错误。catch方法返回的还是一个 Promise 对象,因此后面还可以接着调用then方法。
const someAsyncThing = function() {
return new Promise(function(resolve, reject) {
// 下面一行会报错,因为x没有声明
resolve(x + 2);
});
};
someAsyncThing()
.catch(function(error) { //如果有错误就运行此,没有直接跳过
console.log('oh no', error);
})
.then(function() { //如果此处还有异常,上面catch就不会被执行,因为新的promise没有catch监听,只能顺序监听
console.log('carry on');
});
// oh no [ReferenceError: x is not defined]
// carry on
Promise.prototype.finally()
finally方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
finally方法的回调函数不接受任何参数,这意味着没有办法知道,前面的 Promise 状态到底是fulfilled还是rejected。这表明,finally方法里面的操作,应该是与状态无关的,不依赖于 Promise 的执行结果
finally本质上是then方法的特例。
promise
.finally(() => {
// 语句
});
// 等同于
promise
.then(
result => {
// 语句
return result;
},
error => {
// 语句
throw error;
}
);
Promise.all()
Promise.all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.all([p1, p2, p3]);
p的状态由p1、p2、p3决定,分成两种情况。
(1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
Promise.race()
Promise.race方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.race([p1, p2, p3]);
上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
典型应用:
如果指定时间内没有获得结果,就将 Promise 的状态变为reject,否则变为resolve。
const p = Promise.race([
fetch('/resource-that-may-take-a-while'), //真实网络请求的promise,如果超时前返回,如下的定时promise就不会被执行
new Promise(function (resolve, reject) {//超时的promise
setTimeout(() => reject(new Error('request timeout')), 5000)
})
]);
p.then(console.log).catch(console.error);
Promise.resolve()
将现有对象转为 Promise 对象,Promise.resolve方法就起到这个作用。
const jsPromise = Promise.resolve($.ajax('/whatever.json'));
Promise.resolve方法的参数分成四种情况。
(1)参数是一个 Promise 实例
如果参数是 Promise 实例,那么Promise.resolve将不做任何修改、原封不动地返回这个实例
(2)参数是一个thenable对象
thenable对象指的是具有then方法的对象,比如下面这个对象。
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
Promise.resolve方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then方法。
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
let p1 = Promise.resolve(thenable);
p1.then(function(value) {
console.log(value); // 42
});
(3)参数不是具有then方法的对象,或根本就不是对象
如果参数是一个原始值,或者是一个不具有then方法的对象,则Promise.resolve方法返回一个新的 Promise 对象,状态为resolved。
const p = Promise.resolve('Hello');
p.then(function (s){
console.log(s)
});
// Hello
上面代码生成一个新的 Promise 对象的实例p。由于字符串Hello不属于异步操作(判断方法是字符串对象不具有 then 方法),返回 Promise 实例的状态从一生成就是resolved,所以回调函数会立即执行。Promise.resolve方法的参数,会同时传给回调函数。
(4)不带有任何参数
Promise.resolve方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象。
立即resolve的 Promise 对象,是在本轮“事件循环”(event loop)的结束时,而不是在下一轮“事件循环”的开始时。
setTimeout(function () {
console.log('three');
}, 0);
Promise.resolve().then(function () {
console.log('two');
});
console.log('one');
// one
// two
// three
上面代码中,setTimeout(fn, 0)在下一轮“事件循环”开始时执行,Promise.resolve()在本轮“事件循环”结束时执行,console.log('one')则是立即执行,因此最先输出。
Promise.reject()
Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。
const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))
p.then(null, function (s) {
console.log(s)
});
// 出错了
