一般我们的javascript都是同步运行的,但是这样在前端上就会出现请求空白期,在一般web开发中我们是使用ajax来解决异步的问题。但是如果我们需要的异步操作层级很多,ajax用起来就会比较繁琐。这时候我们就可以使用promise了。
在本轮 事件循环 运行完成之前,回调函数是不会被调用的。
即使异步操作已经完成(成功或失败),在这之后通过 then() 添加的回调函数也会被调用。
通过多次调用 then() 可以添加多个回调函数,它们会按照插入顺序进行执行。
promise有三种状态:pending(进行中)、fulfilled(已成功)、rejected(已失败)
.then() 用来处理成功回调
.catch() 用来处理失败回调
.finally() 最后收尾处理无论成功与否都会被调用
先来看一个小案例
const p=new Promise((resolve, reject)=>{
//这里模拟进行异步执行
console.log("开始异步请求");
setTimeout(() => {
//如果异步任务执行成功了调用resolve,失败了调用reject
console.log("异步请求结束");
resolve("结果数据"); //设置为成功
}, 3000);
}).then(message=>{
console.log("调用成功:"+message);
}).catch(error=>{
console.log("调用失败:"+error);
});
分开写法
const p2=new Promise((resolve, reject)=>{
//这里模拟进行异步执行
console.log("开始异步请求");
setTimeout(() => {
//如果异步任务执行成功了调用resolve,失败了调用reject
console.log("异步请求结束");
resolve("结果数据"); //设置为成功
}, 3000);
});
p2.then(message=>{
console.log("调用成功:"+message);
}).catch(error=>{
console.log("调用失败:"+error);
});
在上面的案例中异步请求完成之后,如果成功则调用 resolve() 如果失败调用 reject() 。他们同时可以传输数据到相应的接收函数。其中, resolve() 调用的是 then() ,reject() 调用的是 catch() 。
有时候我们需要异步请求之后,多次进行数据操作,可以使用 promise 的链式操作来达到目的。
const p=new Promise((resolve, reject)=>{
console.log("开始异步请求1");
setTimeout(() => {
console.log("异步请求结束1");
resolve("结果数据"); //设置为成功
}, 3000);
}).then(message=>{
console.log("调用成功1:"+message);
message+='aaaa';
return message;
}).then(message2=>{
console.log("调用成功2:"+message2);
message2+='bbb';
return message2;
}).then(message3=>{
console.log("调用成功2:"+message3);
}).catch(error=>{
console.log("调用失败:"+error);
});
上面案例中,通过多个 then() 来链式调用。
TIP:在 then() 中,如果想继续保存数据链式调用,需要 return 数据到下一个then,否则数据会丢失。
有时候我们需要连续多次异步请求,比如拿到用户ID查手机号,拿到手机号去查运营商等等。这时候就要用到 promise 异步链式调用,这也是promise的一个特色。
const p=new Promise((resolve, reject)=>{
console.log("开始异步请求1");
setTimeout(() => {
console.log("异步请求结束1");
resolve("结果数据"); //设置为成功
}, 1000);
}).then(message=>{
console.log('第一个then:'+message);
//上面请求成功拿到数据之后,再次异步请求
console.log("开始异步请求2");
return new Promise((resolve, reject)=>{
//第二次异步请求
setTimeout(() => {
//调用成功函数
console.log("异步请求结束2");
resolve('第一个then中的数据');
}, 1000);
})
}).then(message2=>{
console.log('第二个then:'+message2);
//上面请求成功拿到数据之后,再次异步请求
}).catch(error=>{
console.log("调用失败:"+error);
});
上面的代码中我们可以看到,前一个函数 return 到后面的是一个 promise对象,所以可以连续进行异步请求。
在链式异步请求调用中,如果中间有一个调用失败了,后续的异步请求都不会继续执行,而是直接进入 catch() 。
const p=new Promise((resolve, reject)=>{
console.log("开始异步请求1");
setTimeout(() => {
console.log("异步请求结束1");
resolve("结果数据"); //设置为成功
}, 1000);
}).then(message=>{
console.log('第一个then:'+message);
//上面请求成功拿到数据之后,再次异步请求
console.log("开始异步请求2");
return new Promise((resolve, reject)=>{
//第二次异步请求
setTimeout(() => {
//调用成功函数
console.log("异步请求结束2");
reject('第一个then中的数据');
}, 1000);
})
}).then(message2=>{
console.log('第二个then:'+message2);
//上面请求成功拿到数据之后,再次异步请求
}).catch(error=>{
console.log("调用失败:"+error);
}).finally(()=>{
console.log("end");
});
TIP:如果不是链式异步请求无法调用 reject 可以直接调用 throw 来直接报错进入 catch()。
如果我们有多个同时运行的异步请求,需要全部完成之后才处理。这时候可以用 promise.all 来处理
Promise.all([
new Promise((resolve, reject)=>{
setTimeout(() => {
resolve('OK1');
}, 1000);
}),
new Promise((resolve, reject)=>{
setTimeout(() => {
resolve('OK2');
// reject('err2');
}, 2000);
}),
]).then(data=>{
console.log(data[0]);
console.log(data[1]);
}).catch(err=>{
console.log(err)
})
上面的代码中,2个 promise 对象都要完成异步请求之后,才会触发 then()。 如果其中任何一个触发失败,都会直接进入 catch()