es6的promise
Last Updated:2022-08-12
阅读promise总结
promise三种状态,并且一旦状态改变后,就不会再变
有三种状态:pending
(进行中)、fulfilled
(已成功)和rejected
(已失败)。
Promise
对象的状态改变,只有两种可能:从pending
变为fulfilled
和从pending
变为rejected
。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise
对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
promise缺点
- 首先,无法取消
Promise
,一旦新建(new)
它就会立即执行,无法中途取消。(不是有then才执行,而是new就会执行) - 其次,如果不设置回调函数,
Promise
内部抛出的错误,不会反应到外部。第三,当处于pending
状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
then
函数吃2个参数
- 第一个回调函数是
Promise
对象的状态变为resolved
时调用, - 第二个回调函数是
Promise
对象的状态变为rejected
时调用。
这两个函数都是可选的,不一定要提供。它们都接受Promise
对象传出的值作为参数。
getJSON("/post/1.json").then(
post => console.log("resolved: ", post),
err => console.log("rejected: ", err)
);
then
函数返回的是一个新的 Promise
实例(不是原来的那个Promise实例)
因此可以采用链式写法,即then
方法后面再调用另一个then
方法。
getJSON("/posts.json").then(function(json) {
return json.post;
}).then(function(post) {
// ...
});
then
指定的回调函数(即入参)是异步的
then
指定的回调函数,将在当前脚本所有同步任务执行完后才执行。
- step1, 先运行下述代码:
const delay1s = new Promise(function (resolve, reject) {
setTimeout(() => resolve('resolved'), 1000)
})
- step2, 等step1代码运行后,过2s,再运行下述代码
delay1s.then((v1) => {
console.log('then的回调,执行了,执行结果是: ' + v1)
});
console.log('此处,比then的回调先执行;因为虽然已经过了2s,即delay1s的状态,已经是resolved了,此时then能马上获知状态是resolved,但是,then的回调函数还是被异步了,所以,此段文字会在then的回调之前执行')
相当于then的回调函数func1,被setTimeout(func1, 0)
(可能不是100%精准,可能会设计到this指向问题,但举这个例子,是取其异步的意思)
resolve的参数如果是另一个异步操作(即是一个promise实例)resolve(new Promise(...))
此时第一个promise实例(p1),会等待resolve入参的promise实例(p2)的状态变换而变换(即采用p2的状态)。即,p2变成resolved
了,那么,p1的状态也变成resolved
;p2变成rejected
,那么p2也变成rejected
;
举例:
const p1 = new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('fail')), 3000)
})
const p2 = new Promise(function (resolve, reject) {
setTimeout(() => resolve(p1), 1000)
})
p2
.then(result => console.log(result))
.catch(error => console.log(error))
// Error: fail
p1
是一个 Promise,3 秒之后变为rejected
。p2
的状态在 1 秒之后改变,resolve
方法返回的是p1
。由于p2
返回的是另一个 Promise,导致p2
自己的状态无效了,由p1
的状态决定p2
的状态。所以,后面的then
语句都变成针对后者(p1
)。又过了 2 秒,p1
变为rejected
,导致触发catch
方法指定的回调函数。
Promise.prototype.catch()
方法是.then(null, rejection)
或.then(undefined, rejection)
的别名,用于指定发生错误时的回调函数。
Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch
语句捕获。
getJSON('/post/1.json').then(function(post) {
return getJSON(post.commentURL);
}).then(function(comments) {
// some code
}).catch(function(error) {
// 处理前面三个Promise产生的错误
});
上面代码中,一共有三个 Promise 对象:一个由getJSON()
产生,两个由then()
产生。它们之中任何一个抛出的错误,都会被最后一个catch()
捕获。
一般来说,不要在then()
方法里面定义 Reject 状态的回调函数(即then
的第二个参数),总是使用catch
方法。
跟传统的try/catch
代码块不同的是,如果没有使用catch()
方法指定错误处理的回调函数,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
上面代码中,someAsyncThing()
函数产生的 Promise 对象,内部有语法错误。浏览器运行到这一行,会打印出错误提示ReferenceError: x is not defined
,但是不会退出进程、终止脚本执行,2 秒之后还是会输出123
。这就是说,Promise 内部的错误不会影响到 Promise 外部的代码,通俗的说法就是“Promise 会吃掉错误”。
不过,Node.js 有一个unhandledRejection
事件,专门监听未捕获的reject
错误,上面的脚本会触发这个事件的监听函数,可以在监听函数里面抛出错误。
process.on('unhandledRejection', function (err, p) {
throw err;
});
上面代码中,unhandledRejection
事件的监听函数有两个参数,第一个是错误对象,第二个是报错的 Promise 实例,它可以用来了解发生错误的环境信息。
注意,Node 有计划在未来废除unhandledRejection
事件。如果 Promise 内部有未捕获的错误,会直接终止进程,并且进程的退出码不为 0。
const promise = new Promise(function (resolve, reject) {
console.log('最早的:new Promise')
resolve('第三: ok: before error test');
setTimeout(function () {
throw new Error('第四,但已经是另一个环境了,所以会无法捕捉,从而下面的代码不执行test');
console.log('代码不会执行到这里,被throw new Error异常掉了')
}, 0)
});
promise.then(function (value) { console.log(value) });
console.log('第二早的')
Promise
实例p1
,p1.catch(...)
产生的新实例的p2
的状态是fullfilled
(即resolved), p1
的状态是rejected
(假设被catch了)
注意,如果作为参数的 Promise 实例,自己定义了catch
方法,那么它一旦被rejected
,并不会触发Promise.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: 报错了]
上面代码中,p1
会resolved
,p2
首先会rejected
,但是p2
有自己的catch
方法,该方法返回的是一个新的 Promise 实例,p2
指向的实际上是这个实例。该实例执行完catch
方法后,也会变成resolved
,导致Promise.all()
方法参数里面的两个实例都会resolved
,因此会调用then
方法指定的回调函数,而不会调用catch
方法指定的回调函数。
如果p2
没有自己的catch
方法,就会调用Promise.all()
的catch
方法。
const p1 = new Promise((resolve, reject) => {
resolve('hello');
})
.then(result => result);
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: 报错了
const p = Promise.resolve('Hello');
p.then(function (s) {
console.log(s)
});
setTimeout(()=> {
console.log(`setTimeout, after Promise的异步`)
}, 0)
console.log('before resolve')