promise是一种异步编程解决方案,回调函数也可以解决异步编程问题,如果层次过深,会出现回调地狱问题
- 下面的代码,先打印2再打印1 因为定时器是异步执行的,互不影响,所以时间短的先执行
function fn1() {
setTimeout(function () {
console.log(1);
}, 1000)
}
function fn2() {
setTimeout(function () {
console.log(2);
}, 500)
}
fn1();
fn2();
- 下面的代码,先打印1再打印2,回调函数也可以解决异步编程问题,
function fn1(fn) {
setTimeout(function () {
console.log(1);
fn();
}, 1000)
}
function fn2() {
setTimeout(function () {
console.log(2);
}, 500)
}
fn1(fn2);
- 回调函数虽然可以解决异步编程问题,但是如果层次过深,会出现回调地狱问题
function fn3() {
setTimeout(function () {
console.log(3);
setTimeout(function () {
console.log(2);
setTimeout(function () {
console.log(1);
}, 1000)
}, 1000)
}, 100);
}
fn3();
- promise是一个对象,通过new promise来创建对象
- 创建promise对象,promise需要接收一个函数作为参数,作为参数的函数有两个参数 resolve,reject 这两个参数也是函数
- promise对象有三种状态:进行中(pending),成功(fullfilled),失败(rejected)
- 调用resolve方法等价于调用then方法的第一个参数,调用了reject方法,等价于调用then方法的第二个参数
- then 方法可以接收两个函数作为参数,第一个作为参数的函数,执行成功状态的回调,第二个作为函数的参数,执行失败状态的回调
let p = new Promise(function (resolve, reject) {
resolve();//调用resolve方法等价于调用then方法的第一个参数
});
p.then(function () {
//成功
console.log(1); //1
}, function () {
///失败
console.log(2);
});
let p = new Promise(function(resolve,reject){
setTimeout(function(){
resolve(100);//可以传参,成功状态,所以执行then方法的第一个作为参数的函数
//reject(); //失败状态
},1000);
});
p.then(function(data){//在这里接收参数
console.log(data);
},function(data){
console.log(data);
});
- promise解决异步回调问题
function fn() {
let p = new Promise((resolve, reject) => {
setTimeout(() => {
console.log("我的定时器是1s");
resolve();//成功
}, 1000);
});
return p;
}
function fn1() {
setTimeout(() => {
console.log("我的定时器是0.5s");
}, 500);
}
fn().then(fn1);//调用fn函数返回Promise对象
- then()方法
- then方法可以接收两个函数作为参数
- 第一个作为参数的函数:成功状态的回调
- 第二个作为参数的函数:失败状态的回调
注意:then方法会返回一个新的promise,由于返回值是promise,所以then方法可以实现连缀效果。then方法若只接收了一个参数,则这个参数是成功回调。
then方法的第一个参数函数的返回值问题:
1)若返回值是一个非promise,则这个数据会作为参数传递给第二个then
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve();//成功
}, 1000);
});
p.then(() => {
return "hello";
}).then((data) => {
console.log(data);//hello
});
2)若返回值是一个promise,则第二个then状态的改变取决于这个promise
示例:利用promise解决异步回调问题
function fn1(){
const p = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve("我是1s");
},1000);
});
return p;
}
function fn2(){
const p = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve("我是0.5s");
},500);
});
return p;
}
function fn3(){
const p = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve("我是0.1s");
},100);
});
return p;
}
fn1().then((data)=>{
console.log(data);
return fn2();
}).then((data)=>{
console.log(data);
return fn3();
}).then((data)=>{
console.log(data);
});
function fn(num) {//封装,把变量提取出来
const p = new Promise((resolve, reject) => {
setTimeout(() => {
console.log(num);
resolve();
}, 1000);
});
return p;
};
fn(1).then(() => {
return fn(2);
}).then(() => {
return fn(3)
}).then(()=>{
console.log("over");
})
- promise的异常处理:catch方法
- 捕获,catch方法可以捕获promise执行过程中的错误,也可以捕获promise的失败状态
- catch也需要接收一个函数作为参数
- 由于catch既可以接收promise的失败状态,又可以捕获promise中的代码异常信息,所以推荐:在then中接收成功结果,在catch中接收失败和异常结果
let p = new Promise((resolve,reject)=>{
console.log(1);
// resolve("ok");
throw new Error("错误");
// reject("no");
});
p.then((data)=>{
console.log(data);
}).catch((err =>{
console.log(err);
}));
- finally方法
- 不管promise的状态是成功还是失败,均会执行finally中的代码
let p = new Promise((resolve,reject)=>{
if(true){
resolve(1);
}else{
reject(2);
}
});
p.then(data=>{
console.log(data);
}).catch(err=>{
console.log(err);
}).finally(()=>{
console.log("开始下一步");
});
- Promise.all()方法
- all方法可以把多个promise实例包装成一个新的promise实例
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1);
}, 1000);
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
// reject(2);
resolve(2);
}, 2000);
});
console.time();
const p = Promise.all([p1, p2]);
p.then((data) => {
//都为成功状态才会执行
console.log(data);//返回数组[1,2]
console.timeEnd();//2s
}).catch(err => {
console.log(err);
});
- 总结:上面示例中,all方法将p1和p2封装为了一个新的promise实例,这个新的promise实例的状态取决于p1和p2的状态,分两种情况:
- 第一种情况:若p1和p2都变为成功状态,则新的promise也会变成成功状态,并且,会把p1和p2的成功的结果封装为数组在新的promise中收到
- 第二种情况:若p1或者p2有一个变为了失败状态,则新的promise会立即变为失败状态
- Promise.race()方法
- race方法也会将多个promise封装为一个新的promise实例。新的promise实例的改变取决于最快发生改变的那个promise的状态。
let p1 = new Promise((resolve, reject) => {
let img = new Image();//实例化一个img对象
img.src = "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1601216995406&di=06e5480673224233af7409be12eba521&imgtype=0&src=http%3A%2F%2Fc.hiphotos.baidu.com%2Fzhidao%2Fpic%2Fitem%2F3ac79f3df8dcd100501e1ae0708b4710b8122f75.jpg";
img.title = "图片1";
img.onload = function () {//图片加载完毕
resolve(img);
}
img.onerror = function () {//图片加载失败
reject("图片加载失败!!!");
}
});
let p2 = new Promise((resolve, reject) => {
let img = new Image();//实例化一个img对象
img.src = "";
img.title = "图片2";
img.src = "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1601217068347&di=b21f50971c496ce7ecef47944753f1a4&imgtype=0&src=http%3A%2F%2Fa2.att.hudong.com%2F84%2F39%2F01300000957523128228398329921.jpg";
img.onload = function () {//图片加载完毕
resolve(img);
}
img.onerror = function () {//图片加载失败
reject("图片加载失败!!!");
}
});
let p = Promise.race([p1, p2]);
p.then(data => {
console.log(data);
}).catch(err => {
console.log(err);
});