简介
Promise简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
Promise有三种状态:pending、fulfilled、rejected
Promise对象初始化时状态为:pending(进行中)
调用resolve方法时,Promise的状态由pending变为fulfilled
调用rejected方法时,Promise的状态由pending变为rejected
1、 Promise对象的状态不受外界影响,只有异步操作的结果可以决定当前时哪一种状态,任何其他操作都无法改变这个状态。
2、 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从
pending变为fulfilled和从pending变为rejected。
使用
简单的初始化一个Promise
const promise = new Promise((resolve) => {
setInterval(() => {
resolve('延迟2s回调');
}, 2000);
});
上面初始化了一个Promise对象,并创建了一个异步操作:在两秒后调用resolve方法。这里仅仅是初始化了这个对象,并没有调用。继续调用这个promise对象
const promise = new Promise((resolve) => {
setInterval(() => {
resolve('延迟2s回调');
}, 2000);
});
promise.then((data) => {
console.log(data);
});
当执行代码的时候,控制台在两秒钟后打印出了“延迟2s回调”,也就是说在上面初始化Promise的resolve里面的参数回调到了下面then方法中的data参数中。简单来说就是then里面的函数,会传递到Promise中并由resolve来控制这个函数的执行时间以及执行所需要的参数并回调。
上述代码的执行过程就是Promise内部状态由pending变为fulfilled。
Promise还有从pending变为rejected的过程。
const promise = new Promise((resolve, reject) => {
setInterval(() => {
let number = getRandomNumber();
if(number > 5) {
resolve('大于5');
} else {
reject('不大于5');
}
}, 2000);
});
promise.then((data) => {
console.log('then');
console.log(data);
}).catch((error) => {
console.log('catch');
console.log(error);
});
在Promise中初始化了一个简单逻辑,两秒之后获得一个随机数字,该数字大于5的时候调用resolve方法,反之则调用reject方法。由上面的例子可以得知,当数字大于5的时候会在then中打印。那么当数字小于5的时候,则会在catch中打印改数字。
简单总结下来,resolve方法回调至then中,reject方法回调至catch中。
那么可以借用Promise的特点,简单的封装一个网络请求。使用resolve和reject分别包装正常返回和异常返回的值和信息。
const promise = new Promise((resolve, reject) => {
fetch(url)
.then((response) => {
return response.json();
})
.then((responseData) => {
resolve(responseData);
})
.catch(function (error) {
reject(error)
})
})
promise.then((data) => {
// 请求返回的数据
}).catch((error) => {
// 请求报错
})
链式调用
通过上面的例子Promise可以理解为是一个类似延迟加载的异步回调函数,那么其实也可以用普通的方法实现Promise的功能
myPromise = (callback) => {
setInterval(() => {
console.log('执行完成');
callback('随便什么数据');
}, 2000);
}
myPromise((data) => {
console.log(data);
})
它也会和Promise拥有同样的效果。那Promise到底能实现什么不可替代的功能呢?
假如有许多的异步操作需要执行,比如连续的三个请求,每一次请求都需要从前一个请求中获取参数,那么它的写法如下
fetch(url1).then((data1) => {
if (data1 == 200) {
fetch(url2).then((data2) => {
if(data2 == 200) {
fetch(url3) ....
...
}
})
}
})
虽然可以实现需求,然是代码看起来并不那么友好,它庞大的层级结构使之无法轻易被修改。如果要是需要在第二个请求和第三个请求中再加入一些异步操作,那么将是不可修改的,这被称为回调地狱(Callback hell)。
使用Promise即可解决上述问题,Promise的优势在于,可以在then方法中继续写Promise对象并返回,然后继续调用then来进行回调操作。也就是说可以一直return一个Promise对象,可以一直在在后面调用then方法。如果使用Promise后上述的需求可以写为
// 分别将三个请求包装为函数
request1 = () => {
return fetch(url1);
}
request2 = () => {
return fetch(url2);
}
request3 = () => {
return fetch(url3);
}
// 调用时
request1().then((data)=> {
return request2();
}).then((data) => {
return request3();
}).then((data) => {
...
}).catch();
只需要在then中调用下一个Promise,上一个Promise中回调的数据即可传递下去。
关于回调地狱(Callback hell)的问题也可以使用async/await解决,类似的代码如下
task() async { try{ String id = await request1(); String userInfo = await request2(); await request3(); //执行接下来的操作 } catch(e){ //错误处理 print(e); } }
相关API
all
Promise.all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.all([promise1, promise2, promise3]);
p的状态由promise1、promise2、promise3决定,分成两种情况。
(1)只有promise1、promise2、promise3的状态都变成fulfilled,p的状态才会变成fulfilled,此时promise1、promise2、promise3的返回值组成一个数组,传递给p的回调函数。
(2)只要promise1、promise2、promise3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
注意,如果作为参数的 promise 实例,自己定义了
catch方法,那么它一旦被rejected,并不会触发Promise.all()的catch方法。
race
Promise.race方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.race([promise1, promise2, promise3]);
上面代码中,只要有任何一个promise改变状态,那么p的状态也会跟着改变。也就是说最快改变状态的promise会让其他promise不会回调。
可以通过这个方法来完成一个请求超时功能
const p = Promise.race([
fetch(url),
new Promise((resolve, reject) => {
setTimeout(() => reject(new Error('request timeout')), 5000)
})
]);
p
.then(console.log)
.catch(console.error);
即当5s内请求没有返回结果,就返回请求超时。