什么是Promise
MDN定义:Promise对象用于异步操作,他表示一个尚未完成且预计在未来完成的异步操作。
Promise
是异步编程的解决方案,相较传统的解决方案更加合理和强大。
回调函数
request('value1', '', function(data1) {
console.log('第一次请求成功', data1);
request('value2', data1, function (data2) {
console.log('第二次请求成功', data2);
request('value3', data2, function (data3) {
console.log('第三次请求成功', data3);
//request... 继续请求
}, function(error3) {
console.log('第三次请求失败', error3);
});
}, function(error2) {
console.log('第二次请求失败', error2);
});
}, function(error1) {
console.log('第一次请求失败', error1);
});
上述例子出现了多层嵌套,这就是常说的回调地狱,而使用Promise
,可以利用then
进行链式回调,将异步操作以同步操作的流程表示出来。
sendRequest('value1', '').then(function(data1) {
console.log('第一次请求成功', data1);
return sendRequest('value2', data1);
}).then(function(data2) {
console.log('第二次请求成功', data2);
return sendRequest('value3', data2);
}).then(function(data3) {
console.log('第三次请求成功', data3);
}).catch(function(error) {
//catch捕捉前面的错误
console.log('sorry, 请求失败了', error);
});
Promise
有三种状态
pending
:初始值fulfilled
:操作成功rejected
:操作失败
Promise
的缺点:
Promise
一旦新建就会执行,无法中途取消- 若不设置回调函数,
Promise
内部抛出错误,不会反映到外部 - 处于
pending
状态时,无法得知目前是刚开始还是即将完成
Promise的基本用法
const promise = new Promise((resolve,reject)=>{
if(/*异步操作成功*/){
resolve(res);
}else{
/*异步操作失败*/
reject(error);
}
})
我们构建一个Promise
,Promise
接受一个函数作为参数,该函数的两个参数分别是resolve
和reject
。它们是两个函数,由JavaScript引擎提供。
resolve
: 异步操作成功时调用,并将操作结果以参数传递出去。
reject
: 异步操作失败时调用,并将操作的报错以参数传递出去。
Promise实例生成后,使用then
方法指定resolved
状态和reject
状态的回调函数
promise.then((value)=>{
//success
},(error)=>{
//failure
});
then
方法可以接受两个回调函数作为参数,第一个函数是promise
对象的状态改变为resolved
是调用,第二个函数是promise
对象状态改变为rejected
时调用。第二个函数为可选。
Promise新建后会立即执行。then
方法中指定的回调函数,将在当前脚本所有同步任务执行完成后才会执行。
const promise = new Promise((resolve,reject)=>{
console.log('success);
resolve();
})
promise.then(() => {
console.log('resolved');
})
console.log('promsie')
// success
// promise
// resolved
promise对象实现ajax操作:
const request = (param) => {
return new Promise ((resolve,reject) => {
var xhr = new XMLHttpRequest();
xhr.open("get", param.url, true);
xhr.send(param.data);
xhr.responseType = "json";
xhr.onreadystatechange = () =>{
if(xhr.readyState=== 4) {
if(xhr.status === 200){
try{
resolve(xhr.responseText + 'success');
}catch(err){
reject(err)
}
}else{
//failure
reject(new Error(xhr.statusText));
}
}
}
})
}
request(param).then((res) => {
console.log(res);
},(error) => {
console.error(error);
})
上述例子,request
是对XMLHttpRequest对象的封装,用于发出一个http请求,并返回一个Promise
对象。
Promise.prototype.then()
then
方法为Prommise实例添加状态改变时的回调函数,即resolved和rejected。
then
方法返回一个新的Promise
对象,因此可以采用链式写法。
Promise.prototype.catch()
catch
方法用于指定发生错误时的回调函数。
request(param).then((res) => {
console.log(res);
}).catch((error) => {
console.error(error);
})
//=========== 等同于 ===============
request(param).then((res) => {
console.log(res);
}).then(undefined||null,(error) => {
console.error(error);
})
Promise抛出一个错误,就被catch的回调函数捕获
const promise = new Promise((resolve,reject) => {
throw new Error('error')
});
promise.catch((error) => {
console.log(error);
})
上面代码的写法等同于
const promise = new Promise((resolve,reject) => {
try{
throw new Error('error')
}catch{
reject(error);
}
});
从以上代码可以看出,reject
方法等同于抛出错误。
Promise对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。
const promise = new Promise((resolve,reject) => {
resolve(a + 2) //此处报错 a未定义
});
promise.then(() => {
console.log('promise');
})
setTimeout(() => {
console.log('promise test')
},5000)
// a is not defind
// promise test
上面代码中,promise
对象的内部语法有错,浏览器会打印错误提示,但不会终止脚本执行,5秒后会输出promise test
,即promsie
内部的错误不会影响到外部的代码。
Promise.resolve()
Promise.resolve
能将现有的对象转为Promise对象,它可以看做是new Promise()
的快捷方式。
Promise.resolve(value)
//=============等同于===================
new Promise(resolve => {
resolve(value);
})
Promise.reject()
该方法和Promise.resolve()
类似,也会返回一个Promise对象,该对象会进入rejected
状态,
Promise.all()
Promise.all()
用于将多个Promise实例,包装成一个新的Promise实例,该方法接受一个数组作为参数,数组的每一项都为一个Promise,若不是,会调用Promise.resolve()
方法,将参数转换为Promise实例。它的状态由参数中的Promise实例决定。
- 当数组中的Promise实例都为
fulfilled
,它的状态才会变为fulfilled
,并将Promise的返回结果按参数的顺序,存入数组。
var p1 = new Promise(resolve => {
setTimeout(resolve,2000,"frist")
})
var p2 = new Promise(resolve => {
resolve('second')
})
var p3 = new Promise(resolve => {
setTimeout(resolve,500,"third")
})
Promise.all([p1,p2,p3]).then(value => {
console.log(value);
})
//
//["first","second","third"]
- 当数组中的Promise实例其中之一的状态变为
rejected
,它的状态也会变为rejected
,并把第一个被reject
的Promise的返回值传给回调函数。
var p1 = new Promise(resolve => {
setTimeout(resolve,1000,"first")
})
var p2 = new Promise((resolve,reject) => {
setTimeout(reject,500,"second")
})
var p3 = new Promise((resolve,reject) => {
setTimeout(reject,500,"third")
})
Promise.all([p1,p2,p3]).then(value => {
console.log('resolve',value);
},error => {
console.log('reject',error);
})
//
// reject second
Promise.race()
该方法同样用于将多个Promie实例包装成一个新的Promise实例,它与Promise.all()
方法一样,会将不是Promise实例的参数转换为Promise实例。与Promise.all()
不同于,当数组中有一个实例改变了状态,它的状态就会改变,并把率先改变的Promise实例的返回值,传递给回调函数。
var p1 = new Promise((resolve,reject)=>{
setTimeout(reject, 2000,'one');
})
var p2 = new Promise((resolve,reject)=>{
setTimeout(resolve, 1000,'two');
})
Promise.race([p1,p2]).then(value=>{
console.log('resolve',value)
},error=>{
console.log('reject',error)
})
//
// resolve two