目录
promise.then返回的新的promise的结果状态由什么决定?
Promise 是一个对象,是异步编程的一种解决方案,从它可以获取异步操作的消息。
特点
- 对象的状态不受外界影响。只有异步操作的结果可以改变这个状态,任何其他操作都不可以改变这个状态。
- 一旦状态改变,就不会在变,任何时候都可以得到这个结果。Promise 状态的改变只有两种情况:从pedding变为fulfilled或者从pending变为rejected。只要这两种情况发生了,状态就凝固了,就不会在发生改变,会一直保持这个结果,这时就被称为resolved(已定型)。如果改变已经发生,再对 Promise 对象添加回调函数,也会立即运行这个结果。这与事件(Event)不同,事件是如果错过就不会被监听到。
缺点
- 一旦被创建就会立即执行,无法中途取消。
- 如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。
- 当处于 pedding 状态时,无法得知目前进行到哪一步(到底是刚开始还是即将完成)。
作用
- 解决回调地狱
- promise可以支持多个并发的请求,获取并发请求中的数据
promise可以解决异步的问题,本身不能说 promise 是异步的。
Promise.prototype.then()
Promise 实例具有 then 方法,也就是说 then 方法是定义在原型对象 Promise.prototype 上的。它的作用是为 Promise 的实例添加回调函数。then 方法的第一个参数是 resolved 状态的回调函数,第二个参数(可选)是 rejected 状态的回调函数。
then 方法返回的是一个新的 promise 实例(不是原来那个 promise 实例)。
new Promise(function (resolve, reject) {
resolve();
}).then(function () {
return 66;
}).then(function (param) {
console.log(param);
});
// 66
第一个回调函数完成以后,会将返回结果作为参数,传入到第二个回调函数。
promise.then返回的新的promise的结果状态由什么决定?
- 简单表达:有前面 promise 实例的 then 回调函数执行的结果决定
- 详细表达:
- 如果 then 回调跑出异常,那么新 promise 实例状态就会变为 rejected
- 如果返回的是非 promise 的任意值,那么新 promise 实例状态就会变为 resolved,value为前面 then 回调返回的值(无返回值就是 undefined)
- 如果返回的是另一个新的 promise 实例,那么新 promise 实例状态就是新 promise 实例的状态
then() 回调返回的新 promise 实例状态规则,适用于其他会返回新 promise 实例的方法。
new Promise((resolve, reject) => {
resolve(1)
}).then(
value => {
console.log("onResolve1()", value);
throw 5;
// 上面输出: onResolve1() 1 onRject2() 5 onResolve3() 666
// return 2;
// 上面输出: onResolve1() 1 onResolve2() 2 onResolve3() undefined
// return Promise.resolve(333);
// 上面输出: onResolve1() 1 onResolve2() 333 onResolve3() undefined
// return Promise.reject(444);
// 上面输出: onResolve1() 1 onRject2() 444 onResolve3() 666
},
reason => console.log("onRject1()", reason)
).then(
value => console.log("onResolve2()", value),
reason => {
console.log("onRject2()", reason)
return Promise.resolve(666);
}
).then(
value => console.log("onResolve3()", value),
reason => console.log("onReject3()", reason)
)
Promise.prototype.catch()
Promise.prototype.catch 方法是 .then(null, rejection) 的别名,用于指定发生错误时的回调函数(或者说 promise 实例处于 rejected 状态时触发的函数)。
var p = new Promise(function (r1, r2) {
r2();
});
p.then(function () {
}, function () {
console.log(1);
}).catch(function () {
console.log(2);
});
p.then(function () {
}).catch(function () {
console.log(2);
});
// 1
// 2
另外,then() 方法指定的回调函数,如果运行中抛出错误,也会被 catch() 捕获。
const promise = new Promise(function(resolve, reject) {
throw new Error('test');
});
promise.catch(function(error) {
console.log(error);
});
// Error: test
可以看出,reject() 方法的作用等同于抛出错误。
如果 promise 状态已经变为 resolved ,在抛出错误是无效的。
const promise = new Promise(function(resolve, reject) {
resolve('ok');
throw new Error('test');
});
promise
.then(function(value) { console.log(value) })
.catch(function(error) { console.log(error) });
// ok
因为 promise 状态一旦被改变就永久保持,无法再被改变了。
Promise 对象的错误具有“冒泡”性质,会一直向后传递,直至被捕获。
var p = new Promise(function (r1, r2) {
r2();
});
p.then(function () {
}).then(function () {
}).then(function () {
}).then(function () {
}).catch(function () {
console.log(2);
});
// 2
一般来说,不要在 then() 里面定义 Reject 状态的回调函数(即 then 的第二个参数),总是使用 catch() 方法。因为 catch() 不仅可以执行 promise 处于 rejected 状态时方法,还可以捕获 then 方法中执行的错误。
promise 内部的错误代码不会影响到 promise 外部的代码,即使 promise 内部代码有错误,外部代码还是会正常进行,通俗的说法就是“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
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() {
console.log('carry on');
});
// oh no [ReferenceError: x is not defined]
// carry on
如果 then() 里面的方法没有报错,则会跳过 catch 方法继续往后执行。
Promise.resolve()
.catch(function(error) {
console.log('oh no', error);
})
.then(function() {
console.log('carry on');
});
// carry on
Promise.prototype.finally()
finally() 方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。此方法的回调函数不接受任何参数,这就意味着没办法知道前面的 Promise 的状态到底是 fulfilled 还是 rejected。这表明,finally 方法里面的操作和状态无关,不依赖于 Promise 的执行结果。
finally 本质上是 then 方法的特例。
promise
.finally(() => {
// 语句
});
// 等同于
promise
.then(
result => {
// 语句
return result;
},
error => {
// 语句
throw error;
}
);
底层实现
Promise.prototype.finally = function (callback) {
// this -> Promise 实例
let P = this.constructor;
// this 本身是没有 constructor 的,但是 this.__proto__.constructor 有
// this.__proto__.constructor == Promise(构造函数)
return this.then(
value => P.resolve(callback()).then(() => value),
reason => P.resolve(callback()).then(() => { throw reason })
);
};
Promise.resolve(2).finally(() => {
// do something
}).then(p => console.log(p));
// 2
finally 在前面 promise 状态改变之后,执行回调,并将前面 promise 的返回值返回。(finally 是在前面 promise 的 then 回调里面执行)
Promise.all()
Promise.all() 方法用于将多个 promise 实例,包装成一个新的 Promise 实例。
const p = Promise.all([p1, p2, p3]);
Promise.all() 方法接收一个数组作为参数,p1、p2、p3 都是 Promise 实例,如果不是则会调用 Promise.resolve() 将参数转为 Promise 实例,再进一步处理。
p 的状态由 p1、p2、p3 共同决定,分为两种情况:
- 只有 p1、p2、p3 的状态都变为 fulfilled ,p 的状态才会变成 fulfilled 。此时 p1、p2、p3 的返回值组成一个数组传给 p 的回调函数。
- 只要 p1、p2、p3 中只要有一个被 rejected ,p 的状态就变成 rejected ,此时第一个被 reject 的实例的返回值,会传递给 p 的回调函数。
const p1 = new Promise(function (r1, r2) {
r1('p1 response');
});
const p2 = new Promise(function (r1, r2) {
r1('p2 response');
});
const p3 = new Promise(function (r1, r2) {
r1('p3 response');
});
Promise.all([p1, p2, p3]).then(function (posts) {
console.log(posts);
}).catch(function (reason) {
// ...
});
["p1 response", "p2 response", "p3 response"]
const p1 = new Promise(function (r1, r2) {
setTimeout(() => {
r2('p1')
}, 2000);
});
const p2 = new Promise(function (r1, r2) {
setTimeout(() => {
r2('p2')
}, 1000);
});
const p3 = new Promise(function (r1, r2) {
setTimeout(() => {
r2('p3')
}, 3000);
});
Promise.all([p1, p2, p3]).then(function (posts) {
console.log(posts);
}).catch(function (reason) {
console.log(reason);
});
// p2
如果作为参数的 promise 实例,自己定义了 catch 方法,那么一旦它被 rejected ,并不会触发 all() 的 catch 方法。
const p1 = new Promise((resolve, reject) => {
resolve('hello');
}).then(result => result).catch(e => e);
const p2 = new Promise((resolve, reject) => {
throw new Error('报错了');
}).then(result => result).catch(e => e);
Promise.all([p1, p2])
.then(result => {
console.log(result)
})
.catch(e => console.log(e));
// ["hello", Error: 报错了]
const p1 = new Promise((resolve, reject) => {
resolve('hello');
}).then(result => result).catch(e => e);
const p2 = new Promise((resolve, reject) => {
throw new Error('报错了');
}).then(result => result);
Promise.all([p1, p2])
.then(result => {
console.log(result)
})
.catch(e => console.log(e));
// Error: 报错了
代码解读:p1 会 resolved ,p2 首先会 rejected ,但是 p2 有自己的 catch 方法,该方法返回的是一个 promise 实例(此实例状态:p2 的 catch 回调方法没有返回值,或者说是一个 undefined,那么 catch 方法返回新的 promise 实例的状态就是 resolved)。这样一来, Promise.all() 方法参数里面的两个实例都会 resolved,因此调用 all 的 then 方法指定的回调函数,而不会进入 catch 方法。
如果 p2 没有自己的 catch 方法,那么就会进入 all 的 catch 方法。
Promise.allSettled()
Promise.allSettled() 方法接收一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只有等所有这些实例都返回结果,不管状态是 resolved 还是 rejected,包装实例才会结果。
和 all() 的区别,allSettled() 可以确定每个 Promise 实例都已结束,all() 只有在一种情况下可以确定每个 Promise 实例都已结束,就是传入的 Promise 实例都变为 resolved 状态时,才能确定传入的实例都已结束,但是有一个抛出异常时,就无法确定其他的 Promise 实例是否已结束。
该方法返回的新的 Promise 实例,一旦结束,状态总是 fulfilled,不会变成 rejected。状态办成 fulfilled 后,Promise 的监听函数接收的参数是一个数组,每个数组成员对应传入 Promise.allSettled() 的 Promise 实例。
const resolved = Promise.resolve(42);
const rejected = Promise.reject(-1);
const allSettledPromise = Promise.allSettled([resolved, rejected]);
allSettledPromise.then(function (results) {
console.log(results);
});
// [
// { status: 'fulfilled', value: 42 },
// { status: 'rejected', reason: -1 }
// ]
代码解读:Promise.allSettled() 的返回值 allSettledPromise ,状态只能变成 fulfilled 。它的监听函数接收到的参数数组是数组 results。该数组的每个成员都是一个对象,对应传入的两个 Promise 实例。每个对象都有 status 属性,属性值只能是 fulfilled 或 rejected。fulfilled 时对象有 value 属性,rejected 时对象有 reason 属性。
Promise.race()
Promise.race() 同样是将多个 Promise 实例,包装成一个新的 Promise 实例返回。
const p = Promise.race([p1, p2, p3]);
与 all() 不同的是,只要 p1、p2、p3 有一个实例率先改变状态,p 的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给 p 的回调函数。
Promise.any()
该方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回。只要参数有一个变成 fulfilled 状态,包装实例就会变成 fulfilled 状态;如果所有参数都变成 rejected 状态,包装实例就会变成 rejected 状态。
Promise.any() 和 Promise.race() 很像,只有一点不同,就是 Promise.any() 不会因为某一个实例变为 rejected 状态而结束。
Promise.any() 抛出的错误,是一个 AggregateError 实例。(ES6 对应的示例和文档里面不一样...)
var resolved = Promise.resolve(42);
var rejected = Promise.reject(-1);
var alsoRejected = Promise.reject(Infinity);
Promise.any([resolved, rejected, alsoRejected]).then(function (result) {
console.log(result); // 42
});
Promise.any([rejected, alsoRejected]).catch(function (results) {
console.log(results); // [-1, Infinity]
});
// 第一个打印结果无异议,但是第二个打印结果...
// 反正我打印出来的是 AggregateError: All promises were rejected
Promise.resolve()
将现有对象转为一个 Promise 对象,状态为 resolved 。等价于:
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
注意这里只是等价于,并不是“全等”
参数的四种情况:
1)如果参数是 Promise 实例,那么 Promise.resolve 将不做任何修改,原封不动的返回这个实例。
var p = new Promise(function (a, b) {
a(1);
});
Promise.resolve(p).then(function (a) {
console.log(a);
});
// 1
2)参数是一个 thenable 对象。
thenable 对象指的是具有 then 方法的对象:
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
Promise.then() 方法会将这个对象转为 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)参数不是具有 thenable 对象,或根本就不是对象。
Promise.then() 会直接返回一个新的 Promise 对象,状态为 resolved。
4)不带任何参数。
直接返回一个 resolved 状态的 Promise 对象。
立即 resolved 的 Promise 对象,是在本轮“事件循环”的结束时执行。
setTimeout(function () {
console.log('three');
}, 0);
Promise.resolve().then(function () {
console.log('two');
});
console.log('one');
// one
// two
// three
Promise.resolved() 是一个微任务。
Promise.reject()
将现有对象转为一个 Promise 对象,状态为 rejected。
Promise.try()
让同步函数同步执行,异步函数异步执行。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。