在Promise出现前,一般是用回调函数来进行异步编程,但是当我们回调的次数非常多时,回调地狱就产生了,同时执行顺序混乱,这会不利于我们阅读代码。
Pormise就是为了解决回调函数导致的回调地狱问题而出现的
首先,Promise是什么呢?
Promise 语法上来说是一个对象,里面保存着在未来发生的事情的结果(即一个异步操作的结果),通过Promise 可以获取到异步操作的消息。
Promise实例的三种状态
pending(进行中)
fulfilled (已成功)
rejected(已失败)
Promise特点
(1)对象的状态不受外界影响。只有异步操作的结果可以决定当前是哪一种状态,任何其他操作都无法改变这状态。
(2)一旦状态改变,就不会再变。Promise 对象的状态改变,只有两种可能:从pending变为fulfilled ,从pending变为rejected。只有发生其中一种状态变化,状态就不会再变了;这时就被称为 resolved(已定型),如果状态已经发生改变了,这时再对Promise对象添加回调函数,也会立即得到这个结果。
Promise缺点
(1)无法取消 promise,一旦新建它就会执行,无法中途取消。
(2)如果不设置回调函数,promise内部抛出的错误,不会反应到外部。
(3)当处于pending状态时,无法得知目前进展到哪一阶段(刚刚开始还是即将完成)。
Promise优点(相较于回调函数)
(1)解决了回调地狱的问题,使代码变得扁平可读且有利于维护
(2)更好的进行错误处理,通过reject方法把promise状态设置为rejected ,这样就可以在then中捕获或者在catch中捕获,然后执行 ’ rejected ‘ 的回调函数。
创建Promise对象实例的方式
1. new Promise() 创建对象
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
2. Promise.resolve() 创建对象,且状态为fulfilled
const value = 'success' ;
Promise.resolve(value).then(value => {
console.log(value) ;//success
})
3. Promise.reject() 创建对象,且状态为rejected
const value = 'error'
//第一种方式
Promise.reject(value).then(undefined, function (value) {
console.log(value) ;//error
});
//第二种方式
Promise.reject(value).then(null, function (value) {
console.log(value) ;//error
});
//第三种方式(推荐)
Promise.reject(value).catch(e => {
console.log(e) ;//error
})
//第四种方式
Promise.reject(new Error("error!!!"));
Promise常用的方法
(1)Promise.prototype.then( )
then方法是定义在原型对象Promise.prototype上的
作用:为Promise实例添加状态改变时的回调函数
参数:
参数一:resolved状态的回调函数(可选),状态变为resolved时调用
参数二:rejected状态的回调函数(可选),状态变为rejected时调用
返回新的Promise实例,因此可以采用链式写法,在then方法后面再调用另一个then方法。
let promise = new Promise((resolve , reject) => {
resolve('success') ;
}).then(value => {
console.log('1:' , value) ;
return Promise.resolve('success2') ;
}).then(value => {
console.log('2:' , value) ;
})
//1: success
//2: success2
(2) Promise.prototype.catch( )
Promise.prototype.catch( )
是.then(null , rejection)
或 .then(undefined , rejection)
的别名(推荐:把错误写在catch里面,因为它可以捕获前面then方法中出现的错误~)
作用:用于指定发生错误时的回调函数
参数:错误原因
注意:如果Promise已经变成resolved,再抛出错误是无效的
//第一种
const promise = new Promise(function(resolve, reject) {
throw new Error('err');
});
promise.catch(error => {
console.log(error);
});
//第二种
const promise = new Promise(function(resolve, reject) {
try {
throw new Error('err');
} catch(e) {
reject(e);
}
});
promise.catch(error =>{
console.log(error);
});
//第三种
const promise = new Promise(function(resolve, reject) {
reject(new Error('err'));
});
promise.catch(error => {
console.log(error);
});
(3)Promise.prototype.finally( )
作用:不管promise对象最后的状态是什么,都会执行的操作
finally( )方法没有参数,这说明了finally( )方法与状态无关,不依赖于Promise的结果
主要运用:如果不使用finally,则同样的语句需要为成功和失败两种情况各写一次,如果使用了finally方法,则只需要写一次
const promise = new Promise(function(resolve , reject) {
// resolve('success') ;
reject('text') ;
});
promise.then((value) => {
console.log('1:' , value) ;
}).catch((error) => {
console.log('err:',error) ;
}).finally(() => {
console.log('end') ;
})
//Promise状态为 fulfilled 时,
// 1:success
// end
//Promise状态为 reject 时,
// err:text
// end
(4)Promise.all()
作用:将多个Promise实例包装成一个新的Promise实例
参数:Promise实例数组(如果数组中的元素不是Promise实例,会先调用 Promise.resolve( )
方法,将元素转换为Promise实例,再进行下一步操作)
另外,Promise.all()
方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。
const p = Promise.all([p1, p2, p3]);
Promise.all
方法返回的新Promise实例的状态取决于p1、p2、p3的状态:
1、当p1、p2、p3 的状态都是fulfilled,p的状态才会变成fulfilled,这时p1、p2、p3 的返回值组成一个数组,传递给p的回调函数
2、只要p1、p2、p3 其中一个实例的状态变成 rejected,p的状态就会变成rejected,这时第一个被reject的实例的返回值,会传递给p的回调函数
// 当p1、p2、p3 的状态都是 fulfilled 时
const p1 = new Promise(function(resolve , reject) {
resolve('p1:success') ;
})
const p2 = new Promise(function(resolve , reject) {
resolve('p2:success') ;
})
const p3 = new Promise(function(resolve , reject) {
resolve('p3:success') ;
})
const p = Promise.all([p1,p2,p3])
.then(value => console.log(value))
.catch(err => console.log(err))
// [ 'p1:success', 'p2:success', 'p3:success' ]
// 当p2的状态是rejected时
const p1 = new Promise(function(resolve , reject) {
resolve('p1:success') ;
})
const p2 = new Promise(function(resolve , reject) {
reject('p2: text') ;
})
const p3 = new Promise(function(resolve , reject) {
resolve('p3:success') ;
})
const p = Promise.all([p1,p2,p3])
.then(value => console.log(value) )
.catch(err => console.log(err))
//p2: text
当p2实例状态变成rejected,并且p2实例有自己的catch方法,这时p2实例会执行catch方法,catch方法会返回一个新的Promise实例,p2指向的是这个新Promise实例,这个实例执行完catch方法后,也会变成fulfilled状态,导致Promise.all
方法中的所有实例状态都变成了fulfilled,因此会调用then方法指定的回调函数,而不会调用catch方法指定的回调函数
const p1 = new Promise(function(resolve , reject) {
resolve('p1:success') ;
})
const p2 = new Promise(function(resolve , reject) {
reject('p2: text') ;
}).catch(err => err);
const p3 = new Promise(function(resolve , reject) {
resolve('p3:success') ;
})
const p = Promise.all([p1,p2,p3])
.then(value => console.log(value) )
.catch(err => console.log(err))
// [ 'p1:success', 'p2: text', 'p3:success' ]
如果 p2 没有自己的 catch 方法,就会调用Promise.all( )
的catch方法。
(5)Promise.race()
作用:将多个Promise实例包装成一个新的Promise实例(和Promise.all ( )
一样~)
运用:用于在规定时间内,如果请求没有返回结果,则返回失败,否则返回成功。
参数:Promise实例数组(如果数组中的元素不是Promise实例,会先调用 Promise.resolve( )
方法,将元素转换为Promise实例,再进行下一步操作)(和Promise.all ( ) 一样~)
const p = Promise.race([p1, p2, p3]);
Promise.race 方法返回的新Promise实例的状态取决于p1、p2、p3的状态:(与Promise.all( )的区别)
只要 p1, p2, p3 中有一个实例率先改变状态 , p 的状态就变成和那个率先改变的实例的状态一样 ;并把率先改变状态的实例的返回值传递给 p 的回调函数。
const p1 = new Promise(function(resolve , reject) {
setTimeout(() => resolve('p1:success'),1000) ; //最先改变状态
})
const p2 = new Promise(function(resolve , reject) {
setTimeout(() => reject('p2: text'),2000) ;
}).catch(err => err);
const p3 = new Promise(function(resolve , reject) {
setTimeout(() => resolve('p3:success'),3000) ;
})
const p = Promise.race([p1,p2,p3])
.then(value => console.log(value) )
.catch(err => console.log(err))
//p1:success
//我试验了一下,当它们时间一样时,还是输出p1,因为在同步执行中,p1也是最先执行的
还有剩下的方法,大家可以去看看ECMAScript 6 入门
学了辣么多方法,下面来练练手叭(题目网上找滴,侵删)
1、
var promise = new Promise(function(resolve, reject){
setTimeout(function() {
resolve(1) ;
}, 3000)
})
//分析一下一下三种方式的不同?
// 1
promise.then(() => {
return Promise.resolve(2) ;
}).then((n) => {
console.log(n) ;
});
// 2
promise.then(() => {
return 2 ;
}).then((n) => {
console.log(n) ;
});
// 3
promise.then(2).then((n) => {
console.log(n) ;
});
1、输出2,Promise.resolved
返回了一个新的Promise对象,并在下一个事件循环中执行then
2、输出2,直接返回2,不用等到下一个时间循环才执行then,直接在这个循环中执行
3、输出1,因为then和catch方法都期望一个函数作为参数,如果遇到非函数的参数,它们会发生Promise穿透现象,打印上一个Promise的返回值
输出结果:
//2
//2
//1
2、
Promise.resolve().then(() => {
console.log('promise1');
const timer2 = setTimeout(() => {
console.log('timer2')
}, 0)
});
const timer1 = setTimeout(() => {
console.log('timer1')
Promise.resolve().then(() => {
console.log('promise2')
})
}, 0)
console.log('start');
1、首先 Promise.resove( ).then( )
是一个微任务,把它加入到微任务队列
2、接下来执行timer1,它是一个宏任务,把它加入到宏任务队列
3、输出start
4、第一轮宏任务执行完成,开始查看微任务队列中是否存在微任务
5、输出promise1
,timer2是宏任务,将它加入到宏任务队列中
6、微任务执行完成,开始查看宏任务队列中是否存在宏任务
7、执行timer1,输出timer1
,Promise.resove( ).then( )
是一个微任务,把它加入到微任务队列
8、执行微任务,输出promise2
9、最后执行宏任务timer2,输出timer2
输出结果
//start
//promise1
//timer1
//promise2
//timer2
3、
const promise = new Promise((resolve, reject) => {
resolve('success1');
reject('error');
resolve('success2');
});
promise.then((res) => {
console.log('then:', res);
}).catch((err) => {
console.log('catch:', err);
})
1、首先执行resolve('success1')
,执行完毕,promise的状态变成了resolved
2、根据Promise的特点可以知道,Promise的状态一旦发生变化,就不会再变
3、之后执行微任务promise.then( )
,输出then: success1
4、
Promise.reject('err!!!')
.then((res) => {
console.log('success', res)
}, (err) => {
console.log('error', err)
}).catch(err => {
console.log('catch', err)
})
1、首先执行Promise.reject( )
2、因为.then
函数中存在两个参数,其中第一个参数是处理Promise成功状态下的函数,第二个参数是处理Promise失败状态下的函数;所以错误会直接被.then
函数中的第二个参数捕获,输出error err!!!
,而不会被后面的catch捕获
输出结果
//error err!!!
参考文章~