- promise是什么
- promise是ES6中的特性,是异步编程的一种解决方案。
- promise的作用就是对异步请求的封装。
- promise是一个对象,对象和函数的区别就是对象可以保存状态,函数不可以(闭包除外)
- https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise
- 为什么要用promise
- [1]. 在异步请求中,指定回调函数的方式更加灵活
- 旧方式中,回调函数必须在启动异步任务前指定
- promist: 启动异步任务》返回promise对象》给promise对象绑定回调函数(甚至可以在异步任务结束后指定)
- [2]. 支持链式调用,可以解决回调地狱问题
- 解决方案:promise链式调用(不好在还有回调函数)
- 终极解决方案:async/await
- Promise总结
- [1]. Promise的状态一经改变就不能再改变。
- [2]. .then和.catch都会返回一个新的Promise。
- [3]. .catch不管被连接到哪里,都能捕获上层未捕捉过的错误。
- [4]. 在Promise中,返回任意一个非 promise 的值都会被包裹成 promise 对象,例如return 2会被包装为return Promise.resolve(2)。
- [5]. Promise 的 .then 或者 .catch 可以被调用多次, 但如果Promise内部的状态一经改变,并且有了一个值,那么后续每次调用.then或者.catch的时候都会直接拿到该值。
- [6]. .then 或者 .catch 中 return 一个 error 对象并不会抛出错误,所以不会被后续的 .catch 捕获。
- [7]. .then 或 .catch 返回的值不能是 promise 本身,否则会造成死循环。
- [8]. .then 或者 .catch 的参数期望是函数,传入非函数则会发生值穿透。
- [9]. .then方法是能接收两个参数的,第一个是处理成功的函数,第二个是处理失败的函数,再某些时候你可以认为catch是.then第二个参数的简便写法。
- [10]. .finally方法也是返回一个Promise,他在Promise结束的时候,无论结果为resolved还是rejected,都会执行里面的回调函数。且它的回调函数是接收不到Promise的结果的
- [11]. .finally的返回值如果在没有抛出错误的情况下默认会是上一个Promise的返回值
- Promise.all()和Promise.rece()总结
- [1]. Promise.all()的作用是接收一组异步任务,然后并行执行异步任务,并且在所有异步操作执行完后才执行回调。
- [2]. .race()的作用也是接收一组异步任务,然后并行执行异步任务,只保留取第一个执行完成的异步操作的结果,其他的方法仍在执行,不过执行结果会被抛弃。
- [3]. Promise.all().then()结果中数组的顺序和Promise.all()接收到的数组顺序一致。
- [4]. all和race传入的数组中如果有会抛出异常的异步任务,那么只有最先抛出的错误会被捕获,并且是被then的第二个参数或者后面的catch捕获;但并不会影响数组中其它的异步任务的执行。
(一) promise 基础
new Promise((resolve, reject)=>{
//一段耗时的异步操作
//判断操作是否成功
if(true){
resolve('成功');//操作成功,并传递变量
}else{
reject('失败');//操作失败,并传递变量
}
}).then(
(success)=>{console.log(success);}, //操作成功时执行该方法,onResolved
(error)=>{console.log(error);} //操作失败时执行该方法,onRejected
);
- Proimise的回调函数是同步函数
- resolve作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
- reject作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
- promise有三个状态:
1、pending[待定]初始状态
2、fulfilled[实现]操作成功
3、rejected[被否决]操作失败
当promise状态发生改变,就会触发then()里的响应函数处理后续步骤;
promise状态一经改变,不会再变。
Promise对象的状态改变,只有两种可能:
从pending变为fulfilled
从pending变为rejected。
这两种情况只要发生,状态就凝固了,不会再变了。
(二) promise 嵌套
new Promise(
(resolve, reject)=>{
setTimeout(()=>{
resolve('这是第一层');
}, 1000);
}
).then((res)=>{
console.log(res);
new Promise(
(resolve, reject)=>{
setTimeout(()=>{
reject("第二层");
}, 1000);
}
).then((res)=>{
console.log(res);
}).catch((res)=>{
console.log(res + "失败");
});
}).catch((res)=>{
console.log("第一层失败");
});
- 当操作执行失败的时候会进入到catch()方法中,当然也可以在then中进行错误的处理
第一种:reject(‘错误信息’).then(() => {}, () => {错误处理逻辑})
第二种:throw new Error(‘错误信息’).catch( () => {错误处理逻辑})
推荐使用第二种方式,更加清晰好读,并且可以捕获前面所有的错误(可以捕获N个then回调错误)
(三) promise 链式调用
-
.then()和.catch()方法可以返回一个新的Promise实例,所以可以链式调用
-
如果你在 then 中 使用了 return,那么 return 的值会被 Promise.resolve() 包装
new Promise(resolve => { console.log('step 0'); setTimeout(() => { resolve('100'); }, 1000); }).then(value => { //value = 100 //将这个新的promise传递下去 return new Promise(resolve => { console.log('step-1-1'); setTimeout(()=>{ resolve('110'); }, 1000); }).then(value => { //value = 110 关键点在这里 console.log('step-1-2'); return value; //value = 110;// 包装成 Promise.resolve(110) }).then(value => { console.log("step-1-3"); return value; //value = 110 }); }).then(value => { console.log(value); //value = 110 return new Promise(resolve =>{ console.log('step 2'); resolve('120'); }); }).then(value => { console.log("step 2-1"); console.log(value); //vaule = 120 });
-
简化版
new Promise((resolve, reject) => { setTimeout(()=>{ resolve("hello World"); }, 1000); }).then(data => { console.log(data + '11'); //输出:hello world11 return data; }).then(data => { console.log(data);//输出:hello world return data + '222'; }).then(data => { console.log(data);//输出:hello world222 });
(四) promise.all()
- Promise.all([p1, p2, p3])用于将多个promise实例,包装成一个新的Promise实例,返回的实例就是普通的promise
- Promise.all接收一个数组作为参数
数组里可以是Promise对象,也可以是别的值,只有Promise会等待状态改变
当所有的子Promise都完成,该Promise完成,返回值是全部值得数组
有任何一个失败,该Promise失败,返回值是第一个失败的子Promise结果// 1.方法1 function fun1(){ var p = new Promise((resolve, reject)=>{ setTimeout(()=>{ resolve('fun1'); },1000); }); return p; } // 2. 方法2 function fun2(){ var p = new Promise((resolve, reject)=>{ setTimeout(()=>{ resolve({name: 'fun2', arg: 'abc'}); //reject({name: 'fun2', arg: 'abc'}); },2000); }); return p; } Promise.all([fun1(), fun2()]).then((res)=>{ console.log(res); console.log('两个方法都成功'); }).catch(res => { console.log('失败'); console.log(res); });
(五) promise.race()
Promise.race([p1, p2, p3]),当p1,p2,p3中谁最先返回 Promise实例就以谁为准,比如p1最先返回并返回失败,则整体进入catch()方法中,p1最先返回并返回成功,则整体进入then()方法中。
// 1.方法1
function fun1(){
var p = new Promise((resolve, reject)=>{
setTimeout(()=>{
//resolve('fun1');
reject('fun1');
},1000);
});
return p;
}
// 2. 方法2
function fun2(){
var p = new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve({name: 'fun2', arg: 'abc'});
// reject({name: 'fun2', arg: 'abc'});
},2000);
});
return p;
}
Promise.race([fun1(), fun2()]).then((res)=>{
console.log(res);
console.log('成功');
}).catch(res => {
console.log('失败');
console.log(res);
});
(六) 关于promise的几个问题
[1]. 主动/系统抛出异常
-
抛出异常:如果当前是pendding就会变为rejected
-
一个promise实例指定多个成功/失败回调函数,都会被调用
const p = new Promise((resole, reject) => { // 第一种抛出异常 //throw new Error("出错了"); // 第二种抛出,会进入到错误处理函数 throw 3 }); p.then((info) => { console.log(info); }).catch((err) => { console.log('err',err); }); //p状态改变,这里也会被捕获 p.then((info) => { console.log(info); }).catch((err) => { console.log('err1',err); });
[2]. 状态改变和指定回调函数的顺序
- 正常情况下是指定回调函数再改变状态,但也可以先该改变状态在指定回调函数
- 如何先改变状态再调用回调函数?
- ① 在执行器中直接调用resolve()/reject()
- ②延迟更长时间才调用then()
- 什么时候才能得到数据?
- ① 如果先指定的回调,那当状态发生改变时,回调函数就会调用,得到数据
- ②如果先改变的状态,那当指定回调时,回调函数就会调用,得到数据
//先指定回调函数,在改变状态(将计时器去掉就是先改变状态后调用回调函数) new Promise((resolve, reject)=>{ setTimeout(()=>{ resolve(2)//后改变的状态(同时指定数据),异步执行回调函数 },2000) }).then(value=>{ //先指定回调函数,保存当前指定的回调函数 console.log(value); });
[3]. then()返回新的promise的状态
- promise.then()返回的新promise的结果状态由什么决定。
- ①简单表达:由then()指定的回调函数执行的结果决定
- ②详细表达:
- 1.如果抛出异常,新的promise变为rejected,reason为抛出的异常
- 2.如果返回的是非promise的任意值,新promise变为resolved,value为返回的值
- 3.如果返回的是另一个新promise,此promise的结果就会成为新promise的结果
- .then() 是立即执行函数,.then()中的回调函数是异步执行函数
new Promise((resolve, reject) => { resolve(1); }) .then( (value) => { console.log("onResolved1()", value); //情况1: 什么都不返回 //情况2: 返回一个非promise //return { name: "zs" }; //情况3:返回一个新的promise并且状态为fulfilled即成功 //return Promise.resolve(3); //情况4:返回一个新的promise并且状态为rejected即失败 //return Promise.reject(4); //情况5:抛出一个异常或任意的数据 throw 5 }, (reason) => { console.log("onRejected1()", reason); } ) .then( (value) => { //情况1的结果:undefine,即没有实参传入 //情况2的结果:{ name: "zs" } //情况3的结果:3 console.log("onResolved2()", value); }, (reason) => { //情况四的结果:4 //情况五的结果:5 console.log("onRejected2()", reason); } );
[4]. promise串联多个操作任务
-
由于promise的then()返回的是一个新的promise,可以将then()链式调用
-
通过then()的链式调用串联多个同步/异步任务
-
then()中如果是一个异步任务,则需要返回一个新的promise
new Promise((resolve, reject) => { setTimeout(() => { //如果这里是reject(1)则第一个then执行失败的处理函数,第二个then执行的是成功的处理函数还是失败的处理函数与第一个then的返回结果有关,与第一个promise无关 resolve(1); }, 1000); }) .then((value) => { console.log("任务1的结果", value); console.log("执行任务2(同步)"); return 2; }) .then((value) => { console.log("任务2的结果", value); //返回一个新的promise,否则还是最开始的promise return new Promise((resolve, reject) => { setTimeout(() => { console.log("执行任务3(异步)"); //return 3; resolve(3); }, 2000); }); }) .then((value) => { console.log("任务3的结果", value); });
[5]. promise异常穿透
当使用promise的then链式调用时,可以在最后指定失败的回调,前面任何操作出了异常,都会传到最后失败的回调中处理。
new Promise((resolve, reject) => {
reject(1);
})
//then中没有处理错误的回调函数时,默认处理相当于:resason=>Promise.reject(reason)
/*
then(
(value) => {
console.log("onResolved1()", value);
return 2;
},
reason => Promise.reject(reason)
)
*/
.then((value) => {
console.log("onResolved1()", value);
return 2;
})
.then((value) => {
console.log("onResolved2()", value);
return 3;
})
.then((value) => {
console.log("onResolved3()", value);
return 4;
})
.catch((reason) => {
console.log("error:", reason);
});
[6]. 中断promise链
当使用promise的then链式调用时,在中间中断,不再调用后面的回调函数,可以在回调函数中返回一个pendding状态的promise对象
new Promise((resolve, reject) => {
reject(1);
})
.then((value) => {
console.log("onResolved1()", value);
return 2;
})
.catch((reason) => {
console.log("error:", reason);
//返回一个状态为pending的promise,就不会执行后面的then
return new Promise(() => {});
})
.then(
(value) => {
console.log("onResolved4()", value);
return 4;
},
(reason) => {
console.log("onRejected()", reason);
}
);
(七) 简单应用
[1]. 使用promise实现休眠效果
async function sleep(ms) { //async 非必须
return await new Promise(resolve => { //如果不使用 async 则不使用 await
setTimeout(() => {
console.log(ms / 1000 + '秒后');
resolve(1);
}, ms);
// setTimeout(resolve , ms); //简化版
});
}
async function sleepTest() {
await sleep(1000);
console.log(123);
}
sleepTest();