什么是Promise?
Promise是ES6提供的一种解决异步编程的方案。
Promise的本质是一个构造函数,我们可以使用new Promise来创建Promise实例对象,利用Promise对象的方法进行异步编程。
为什么要使用Promise?
1. Promise定义回调函数的方式更灵活。
使用纯回调函数解决异步问题,回调函数必须在一开始就被指定,而Promise的回调函数可以在启动了异步任务后指定,甚至可以在异步任务结束后指定。
2. Promise可以解决回调函数因为嵌套造成的回调地狱。
如果后面的异步操作,需要拿到前面异步操作的数据后才能执行,使用纯回调函数,会出现层层嵌套的问题。造成的结果是代码不易阅读,不易维护。
Promise的三种状态——pending、resolved、rejected
新建一个Promise对象时,初始的状态为pending(等待中),调用resolve(),状态变为resolved/fullfilled(成功)。调用reject(),状态变为rejected(失败)。
状态一旦改变,就无法再改变。也就是说,如果已经调用了resolve()和reject()中任意一个函数,再进行调用,Promise状态不会再发生改变。
Promise的执行流程
简述:一开始,我们通过new Promise()创建一个Promise对象,此时的状态为pending。promise的参数接收两个函数,分别是resolve和reject。在成功的时候调用resolve(),在失败的时候调用reject()。resolve()和reject()中的任意一个被调用后,promise的状态发生对应的改变(resloved/rejected)。resolve()和reject()的调用是promise状态的标识,也是后续异步操作的指路灯。只有在状态确认后,才能进行对应的成功/失败的异步操作,也就是执行then/catch中的回调函数。执行完回调函数后,会返回一个新的Promise对象。
Promise代码例子
const promise=new Promise((reslove,reject)=>{ //执行器函数(executor)--> 同步回调函数
setTimeout(()=>{
if(Date.now()%2==0){
//成功时调用resolve()
reslove('success '+Date.now()); //reslove和reject中都可以传入值,且只能传入一个值
}else{
//失败时调用reject()
reject('failed '+Date.now());
}
},1000)
})
// then和catch会接收在resolve和reject中传入的数据
//1. then可以接收resolve和reject的值(resolve在前,reject在后)
promise.then(value=>{
console.log(value); //onresolved回调函数 这里的value接收的就是'success '+Date.now()
},reason=>{
console.log(reason); //onrejected回调函数 这里的reason接收的就是'failed '+Date.now()
})
//2. catch只能接收reject的值
promise.then(value=>{
console.log(value);
}).catch(reason=>{
console.log(reason);
})
//3. 也可以只接收成功/失败的值
promise.then(value=>{
console.log(value);
})
promise.then(null,reason=>{
console.log(reason);
})promise中是先确定状态还是先确定回调?
我们可以通过定时器的方法人为地改变状态和回调的执行顺序,不管怎样都是可以执行的。但是在程序执行的过程中,永远都是先确认状态,再执行回调函数的。
JavaScript的执行机制宏任务和微任务 与 Promise
我们都知道,JavaScript的执行是单线程的,同步任务会率先一件一件得在主线程完成,而异步任务会进入任务队列,等待同步任务完成后执行。
而任务对列中还分为了宏任务和微任务。
宏任务:script,DOM事件函数,ajax,定时器等。
微任务:promise等。
Promise作为典型的微任务,需要注意的是new Promise中的回调函数(也就是执行器函数)是立即执行的,也就是说执行器函数是一个同步回调函数。而promise.then中回调函数才是异步任务(微任务)。
还有一点需要注意的是,promise.then中的回调函数必须在promise状态确定之后才能执行。下面是一个例子:
const promise1 = new Promise(function(resolve, reject) {
console.log(3);
setTimeout(function(){
resolve('Success!');
console.log(1);
},1000)
});
promise1.then(function(value) {
console.log(value);
console.log(2);
});
//代码输出结果:3 (一秒后) 1 success!2代码开始运行后,先从最大的script宏任务开始,new promise直接执行,输出3,setTimeout放入宏任务对列,紧接着的then被放入微任务队列,而本该先执行的微任务,却反而在宏任务结束之后才执行,这里的执行顺序显然是不符合js的执行机制的。
现在,在这段代码里添加一行代码。
const promise1 = new Promise(function(resolve, reject) {
console.log(3);
setTimeout(function(){
resolve('Success!');
console.log(1);
},1000)
resolve('haha'); //我是那行被添加的代码
});
promise1.then(function(value) {
console.log(value);
console.log(2);
});
//代码输出结果:3 haha 2 (一秒后)1这时,输出的结果就是then在前,setTimeout在后了。这行代码改变的是promise的状态。也就是说,then执行的前提是promise的状态已经被确定。
在没加代码前,虽然在执行机制中then已经可以执行,但是由于promise的状态未确定,导致then无法执行(then很苦恼,到底应该执行成功的回调还是失败的回调呢?),没办法,只好等待定时器结束状态确定下来再执行了。加了这行代码后,状态已经确定,then自然就可以在setTimeout前执行了。并且由于状态一旦确定不能更改,所以setTimeout中的resolve('Success!')已经名存实亡了。