Promise
一个容器,保存着某个未来会结束的事件(通常是一个异步操作)的结果
当有大量的计算时,整个系统会卡住,都会使用异步的方式 [回调地狱: 回调嵌套] 来进行组织代码
回调嵌套不容易找出报错的地方
为解决回调地狱的问题,ES6提供了Promise
栗子:
setTimeout(() => {
console.log("shenme");
})
console.log("liang");
// liang 会比 shenme 先输出
如果想 liang这条事件 基于定时器完成
setTimeout(() => {
console.log("shenme");
console.log("liang");
});
如果liang 这个事件也是异步函数
setTimeout(() => {
console.log("shenme");
}, random()) // random是个随机值,结束时间不确定
setTimeout(() => {
console.log("liang");
}, random())
// 如果想 liang这条事件 基于定时器完成,那么必须将其放在定时器中
setTimeout(() => {
console.log("shenme");
setTimeout(() => {
console.log("liang");
}, random())
}, random())
// 这样的嵌套回调,称为: 回调地狱
为了避免这种情况,可使用Promise
// Promise是个构造函数,所以要new来创建
/*new Promise();*/ // 必须接受回调函数,否则报错
new Promise((resolve, reject) => {
resolve(); // 表示Promise一旦执行,就成功了
reject(); // 那么这条语句就不执行了,因为上一步已经确定了Promise的状态,内部状态不会再改变
});
// 将异步函数放在Promise的内部
new Promise((resolve, reject) => {
setTimeout(() => {
/*console.log("liang");
// 如果顺利执行到这一行,就结束
resolve();*/ // 返回Promise状态
// 但也有可能Promise的内部逻辑出错,导致出错
try {
console.log("liang");
resolve('成功了');
}catch (error) {
reject(error);
}
}, 100)
// 上面的异步事件,会传出一个信息msg,如果成功被第一个函数接受,然后调用,否则调用第二个函数
}).then((msg) => {
if (msg) {
setTimeout(() => {
console.log("梁");
}, 100)
}
}, err => {
})
如果想让事件2传给事件3,就return,如果return的是一个普通数据,所有默认成功,只有return 失败的Promise 对象,才会传出失败
.then((msg) => {
console.log("梁2");
// 如果想让事件2传给事件3,就return
/*return '这是事件2传给事件3的'*/
// 那么如何返回失败的去触发失败事件??return一个失败的Promise,在事件3中log error可见
new Promise((resolve, reject) => reject('事件2失败了'));
return undefined;
}, err => {
})
// 还需要事件3基于事件2发生? 继续用.then()
.then((msg) => {
console.log("梁3");
}, err => {
console.log('事件2失败了');
})
// 如果事件1、2正常结束完,那么默认成功
catch()
以上代码输出:
原因: Promise必须设置一个then 接受回调,当把Promise放在then 中,它是显示失败的,必须显示地去捕获这个错误
new Promise((res, rej) => rej('事件2失败了'))
.then(
msg => {},
err => {
console.log(err)
}
)
// 可用catch 捕获
return new Promise((res, rej) => rej('事件2失败了'))
})
.catch(err => {
console.log(err);
})
【注】Promise 内部的错误是会被其捕获掉的,不会影响后续代码的执行("冒泡"性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个 catch 语句捕获),所以最好写上catch 来捕获这个错误
all()
用于多个Promise实例,包装成一个新的Promise 实现
var p = Promise.all([p1,p2,p3]);
上面代码中,Promise.all 方法接受一个数组作为参数,p1、p2、p3 都是 Promise 对象的实例。
(Promise.all 方法的参数不一定是数组,但是必须具有 iterator 接口,且返回的每个成员都是 Promise 实例。)
p 的状态由 p1、p2、p3 决定,分成两种情况:
(1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
rece
同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
var p = Promise.race([p1,p2,p3]);
只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的Promise实例的返回值,就传递给p的返回值。
如果Promise.all方法和Promise.race方法的参数,不是Promise实例,就会先调用下面讲到的Promise.resolve方法,将参数转为Promise实例,再进一步处理。
reslove()
不管传递什么参数,会立即返回一个状态为成功的Promise
reject()
不管传递什么参数,会立即返回一个状态为失败的Promise
特点:
- 对象状态不受外界影响
- 一旦状态改变,就不会再变,任何时候都可以得到这个结果。
(全封闭的)一旦去启动一个Promise,外部再也不能去改变它内部的状态了
状态
pending: (进行中)初始状态,成功或失败状态。
fulfilled: 意味着操作成功完成。
rejected: 意味着操作失败。
优点
有了 Promise 对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数(回调地狱)。
此外,Promise 对象提供统一的接口,使得控制异步操作更加容易。
缺点
1、无法取消 Promise,一旦新建它就会立即执行,无法中途取消。(全封闭)
2、如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。Promise相当于语法糖,还是需要回调函数,只不过位置改变了
3、当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。(全封闭,无法改变也无法查看)