一、promise简介
promise是异步编程的一种解决办法:语法上来看promise是对象,从它可以获得一部操作的信息;从本意上来讲,它是约定,约定过一段时间给你一个结果。
promise有三种状态:
- pending => 等待态
- fulfiled => 成功态
- rejected => 失败态
回调函数的产生,就是将一个函数作为一个数据传值给另外一个函数,另外的函数调用这个传递过来的函数并调用,那么这个函数就成功成为回调函数了!
//当参数num满足一定的条件就执行传入的函数,这个函数就是回调函数
function fn1(a,fn){
if(a > 15 && typeof fn == 'function'){
fn(); //执行fn
}
}
fn1(16,function(){
console.log('this is a callback');
})
一般情况下,回调嵌套最多一到两层,当我们遇到回调嵌套很多的时候。代码就显得非常的复杂,会给编程带来麻烦,这样情形就叫做回调地狱。
promise的诞生就自带光环,来解决了:
- 回调地狱
- 可以解决异步相关的问题
- 支持多个并发的请求
二、promise使用
promise是一个构造函数,自己身上有call、reject、resolve等方法,原型上面有then、catch等方法。
let p = new Promise((resolve,reject) => {
//下面是异步操作的方法
setTimeout(() => {
console.log('执行完成');
resolve('成功啦');
},2000)
});
Promise构造函数接收一个参数:function,并且这个函数需要传入两个参数:
- resolve:异步操作执行成功后的回调函数
- reject:异步操作执行失败后的回调函数
then链式操作的用法
实质上promise的最称赞的地方是状态,以维护状态和传递状态的方式来使得回调函数能够及时的调用,比传递callback function要简单、灵活,所以应该这样使用promise:
p.then((data) => {
console.log(data);
})
.then((data) => {
console.log(data);
})
.then((data) => {
console.log(data);
});
reject的用法
将promise的状态设置为rejected,这样我们在then中就能捕捉到,然后去执行失败的回调。
let p = new Promise((resolve,reject) => {
//异步操作
setTimeout(() => {
let number = Math.ceil(Math.random() * 10); //生成1--10的随机数,ceil()传回大于等于所给数字的整数
if(number <= 5){
resolve(num);
}else{
reject('这个数字太大了!');
}
},2000);
});
p.then((data) => {
console.log('resolve',data);
},(err){
console.log('reject',err);
})
then中传了两个参数,then方法可以接受两个参数,第一个参数就是resolve的回调,第二个参数就是reject的回调。
catch的使用
我们知道promise对象除了then方法外,还有一个catch方法,它其实就是then的第二个参数的作用,专门用来指定reject回调的。
p.then((data) => {
console.log('resolve',data);
}).catch((err) => {
console.log('reject',err);
})
它的效果和then中的第二个参数的效果是一样的;再执行resolve的回调的时候,如果抛出了异常,并不会报错卡死js代码,而是会到catch方法中。
p.then((data) => {
console.log('resolve',data);
console.log(hhz); //并没有hhz变量
}).catch((err) => {
console.log('reject',err);
})
在上面的案例中,then中的最后一个输出,hhz这个变量并没有定义,如果不是promise,这里就会报错,并程序停止执行,但是promise会继续执行,进入到catch中去,并会爆出错误。
all的用法
promise的all方法提供了并行执行异步操作的能力,并且所有的异步操作执行完成后才执行回调。
let p1 = new Promise( (resolve,reject) =>{
//异步操作
})
let p2 = new Promise( (resolve,reject) =>{
//异步操作
})
let p3 = new Promise( (resolve,reject) =>{
//异步操作
})
let p = new Promise.all([p1,p2,p3])
p.then(() => {
//三个成功,就成功
},() => {
//失败,都算失败
})
有了 all 方法之后,就可以并行执行多个异步操作,并在一个回调中处理所有的返回数据。
race的用法
race的使用场景是:给某个异步操作设置超时时间,并且在超时后执行相应的操作,代码如下:
//请求视频资源
function requestVideo(){
let p = new Promise( (resolve,reject) => {
let video = new Video();
video.onload = function(){
resolve(video);
}
video.src = 'video的路径';
} );
return p;
}
//延时函数,用于给请求计时
function timeout(){
var p = new Promise((resolve, reject) => {
setTimeout(() => {
reject('图片请求超时');
}, 5000);
});
return p;
}
Promise.race([requestImg(), timeout()]).then((data) =>{
console.log(data);
}).catch((err) => {
console.log(err);
});
requestImg函数会异步请求一张图片,我把地址写为"图片的路径",所以肯定是无法成功请求到的。timeout函数是一个延时5秒的异步操作。我们把这两个返回Promise对象的函数放进race,于是他俩就会赛跑,如果5秒之内图片请求成功了,那么遍进入then方法,执行正常的流程。如果5秒钟图片还未成功返回,那么timeout就跑赢了,则进入catch,报出“图片请求超时”的信息。
三、实现自己的promise
1.实现成功和失败的回调
要实现上面代码中的功能,也是promise最基本的功能。首先,需要创建一个构造函数promise,创建一个promisel类,在使用的时候传入了一个执行器executor,executor会传入两个参数:成功(resolve)和失败(reject)。之前说过,只要成功,就不会失败,只要失败就不会成功。所以,默认状态下,在调用成功时,就返回成功态,调用失败时,返回失败态。代码如下:
class Promise {
constructor(executor){
//默认状态是等待
this.status = 'pedding';
this.value = undefined;
this.reason = undefined;
//存放成功的回调
this.onResolveCallbacks = [];
//存放失败的回调
this.onRejectCallbacks = [];
let resolve = (data) => {
//this指向的是实例
if(this.status === 'pedding'){
this.value = data;
this.status = 'resolved';
this.onResolveCallback.forEach(fn => fn());
}
}
let reject = (reason) => {
if(this.status === 'pending'){
this.reason = reason;
this.status = 'rejected';
this.onRejectedCallbacks.forEach(fn => fn());
}
}
try{//执行时可能会发生异常
executor(resolve,reject);
}catch (e){
reject(e);//promise失败了
}
}
}
文章说明:
文章很多学习借鉴了下面这个金主,文章总结的很优秀,所以想保存在自己这边方便查询和回忆。我这个是不全的,想看全内容和更精彩的内容,请访问下面的这位金主。作者:
蔓蔓雒轩来源:掘金
著作权归作者
蔓蔓雒轩所有。商业转载请联系作者获得授权,非商业转载请注明出处。