ES6异步编程解决方案:Promise
用于封装异步操作,获取成功或失败的结果
1. Promise构造函数: Promise(excutor) {}
2. Promise.prototype.then 方法
3. Promise.prototype.catch 方法
4. Promise 的三种状态:Pending初始状态、resolved(fulfilled)成功、rejected失败
5. Promise 指定的then、catch回调为微任务(先执行微任务回调队列,后执行微任务回调队列,如setimeout)
实例化 Promise 对象
如何改变一个Promise实例的状态?
- 执行resolve(value):如果当前是pending就会变为fulfilled
- 执行reject(reason):如果当前是pending就会变为rejected
- 执行器函数(executor)抛出异常:如果当前是pending就会变为rejected(可以手动throw抛异常)
const p = new Promise(function(resolve, reject) {
// 异步操作:
setTimeout(function() {
let data = '获取数据成功';
// 调用resolve函数,来改变 p(Promise) 的状态:(成功)
resolve(data);
},1000);
});
// 调用 promise 对象的 then 方法的第一个回调函数(包含两个成功和失败函数类型的参数)
p.then(function(value) {
console.log(value); // 获取数据成功
}, function(reason) {
console.error(reason);
})
const p = new Promise(function(resolve, reject) {
// 异步操作:
setTimeout(function() {
let err = '获取数据失败';
// 调用reject函数,来改变 p(Promise) 的状态:(失败)
resolve(err);
},1000);
});
// 调用 promise 对象的 then 方法的第二个回调函数(包含两个成功和失败函数类型的参数)
p.then(function(value) {
console.log(value);
}, function(reason) {
console.error(reason); // 获取数据失败
})
Promise 封装
// 引入node.js API 的 fs 模块
const fs = require('fs');
const p = new Promise(function(resolve, reject) {
fs.readFile("./file.text", (err, data)=>{
// 失败:
if(err) reject(err); // 此时p状态为失败
// 成功:
resolve(data); // 此时p状态为成功
});
});
// p状态为成功时调用第一个函数参数,p状态为失败时调用第二个函数参数
p.then(function(value) {
console.log("读取成功!", value); // 成功回调(异步)打印数据:data
}, function(reason) {
console.log("读取失败!", reason); // 失败回调(异步)抛出错误:err
});
console.log("end") // 异步输出结果:end 读取成功!data / end 读取失败!err
Promise 封装 Ajax(解决回调地狱)
优势:
- 指定回调函数的方式更加灵活:
- 旧的:必须在启动异步任务前指定
- promise:启动异步任务→返回promie对象→给promise对象绑定回调函数(甚至可以在异步任务结束后指定)
- 支持链式调用,可以解决回调地狱问题
- 什么是回调地狱:回调函数嵌套调用,外部回调函数异步执行的结果是嵌套的回调函数执行的条件
- 回调地狱的弊病:代码不便于阅读、不便于异常的处理
- 一个不是很优秀的解决方案:then的链式调用
- 终极解决方案:async/await(底层实际上依然使用then的链式调用)
简单封装:
const p = new Promise((resolve, reject)=>{
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = ()=>{
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status < 300){
resolve(xhr.response);
}else {
reject('请求出错!')
}
}
}
xhr.open('GET', 'https://api.xxx');
xhr.responseType = 'json'
xhr.send();
})
// 状态回调:
p.then(
(value)=>{console.log('成功', value);},
(reason)=>{console.log('失败', reason);}
)
优化:
function sendAjax(url,data){
const p = new Promise((resolve,reject)=>{
//实例xhr
const xhr = new XMLHttpRequest()
//绑定监听
xhr.onreadystatechange = ()=>{
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status < 300) resolve(xhr.response);
else reject('请求出问题了');
}
}
//整理参数
let str =''
for (let key in data){
str += `${key}=${data[key]}&`
}
str = str.slice(0,-1)
xhr.open('GET',ur1+'?'+str)
xhr.send()
})
return p
}
then的链式调用解决回调地狱(ES6)
Promise(实例).then()返回的是一个“新的Promise实例”,它的值和状态由什么决定?🔺
-
简单表达:由then()所指定的回调函数执行的结果决定
-
详细表达:
- 如果then所指定的回调返回的是非Promise值a:那么“新Promise实例”状态为:成功(fulfilled), 成功的value为a
- 如果then所指定的回调返回的是一个Promise实例p:那么“新Promise实例”的状态、值,都与p一致
- 如果then所指定的回调抛出异常:那么“新Promise实例”状态为rejected, reason为抛出的那个异常
const x = sendAjax('https://api.xxx01', {page:1, count:1})
x.then(
// 1. then也返回一个Promise对象,return将请求后的Promise对象传给then代表请求成功,再去调用下一个then🔺
/* 2. 失败的回调为reason为undefined,undefined返回给then时状态为成功,需中断Promise链:
在失败的回调函数中返回一个pendding状态的Promise实例,return new Promise(()=>{})
*/
(value) => {console.log('第1次请求成功',value);
return sendAjax('https://api.xxx02',{page:2, count:1}) // 开始下一次请求
},
(reason) => {console.log('第1次请求失败',reason); return new Promise(()=>{})}
)
.then(
value => {
console.log('第2次请求成功',value);
return sendAjax('https://api.xxx03',{page:3, count:1})
},
reason => {console.log('第2次请求失败',reason); return new Promise(()=>{})}
)
.then(
value => {
console.log('第3次请求成功',value);
return sendAjax('https://api.xxx04',{page:4, count:1})
},
reason => {console.log('第3次请求失败',reason);}
)
错误穿透catch:
- 当使用promise的then链式调用时,可以在最后用catch指定一个失败的回调,
- 前面任何操作出了错误,都会传到最后失败的回调中处理了
- 备注:如果不存在then的链式调用,就不需要考虑then的错误穿透
function sendAjax(url,data,index){
const p = new Promise((resolve,reject)=>{
//实例xhr
const xhr = new XMLHttpRequest()
//绑定监听
xhr.onreadystatechange = ()=>{
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status < 300) resolve(xhr.response);
else reject(`第${index}次请求出问题了`); // 错误穿透索引参数
}
}
//整理参数
let str =''
for (let key in data){
str += `${key}=${data[key]}&`
}
str = str.slice(0,-1)
xhr.open('GET',ur1+'?'+str)
xhr.send()
})
return p
}
sendAjax('https://api.xxx01',{page:1},1)
.then(
value => {console.log('第1次请求成功',value); return sendAjax('https://api.xxx02',{page:2},2)}
)
.then(
value => {console.log('第2次请求成功',value); return sendAjax('https://api.xxx03',{page:3},3)}
)
.then(
value => {console.log('第3次请求成功了',value);}
)
.catch(
reason => console.log(reason)
)
其他方法
Promise.prototype.catch方法:(只处理失败回调,需要先实例化对象)
Promise(实例).catch(onRejected) onRejected: 失败的回调函数(reason) => {} 说明: catch方法是then方法的语法糖,相当于: then(undefined, onRejected)
const p = new Promise((resolve,reject)=>{
setTimeout(()=>{
reject(100)
},1000)
})
p.catch(
reason => {console.log('失败',reason);}
)
Promise.resolve方法: (Promise自身方法,不需要实例)
const p = Promise.resolve(value) 说明:用于快速返回一个状态为fulfilled或rejected的Promise实例对象 备注:value值可能是“非Promise值”(p.then成功)或“Promise”(值为失败的Promise时p.then为失败)
const p = Promise.resolve(100)
p.then(
value => {console.log('成功了',value);},
reason => {console.log('失败了',reason);}
)
Promise.reject方法: Promise.reject方法(reason)
说明:用于快速返回一个状态为rejected的Promise实例对象
Promise.all方法: Promise.all(promiseArr)
promiseArr: 包含n个Promise实例的数组[p1,p2,p3...] 说明:返回一个新的Promise实例,只有所有的promise都成功才成功(值为实例返回值数组),只要有一个失败就直接返回失败
Promise.race方法: Promise.race (promiseArr)
promiseArr: 包含n个Promise实例的数组 说明:返回一个新的Promise实例,成功还是很失败?以最先出结果的promise为准
Promise配合async与await使用
<script>
const p1 = new Promise( (resolve, reject)=>{
setTimeout(()=>{
resolve('a')
}, 1000)
})
const p2 = new Promise( (resolve, reject)=>{
setTimeout(()=>{
reject('一些错误')
}, 2000)
})
const p3 = new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve('c')
}, 4000)
})
!(async()=>{
try{
const result1 = await p1
console.log(result1);
const.result2 = await p2
console.log(result2);
const result3 = await p3
console.log(result3);
} catch (error){
console.log(error);
})()
</script>
!(async()=>{
try{
const result1 = await sendAjax('https://api.xxx01',{page:1},1)
console.log('第1次请求成功',result1);
const result2 = await sendAjax('https://api.xxx02',{page:2},2)
console.log('第2次请求成功',result2);
const result3 = await sendAjax('https://api.xxx03',{page:3},3)
console.log('第3次请求成功',result3);
} catch (error){
console.log(error);
})()
1. async修饰的函数:
- 函数的返回值为promise对象
- Promise实例的结果由async函数执行的返回值决定
2. await表达式:
- await右侧的表达式一般为Promise实例对象,但也可以是其它的值
- 如果表达式是promise对象,await后的返回值是promise成功的值
- 如果表达式是其它值,直接将此值作为await的返回值
3. 注意:
- await必须写在async函数中,但async函数中可以没有await
- 如果await的promise失败了,就会抛出异常,需要通过try...catch来捕获处理