1.Promise是什么
1.抽象表达:
- Promise是一门新的技术(ES6规范)
- Promise是js中进行异步编程的新的解决方案
备注:旧方案是单纯的回调函数
2.具体表达:
- 从语法上来说:Promise 是一个构造函数
- 从功能上来说:Promise对象是用来封装异步操作(可获取成功/失败结果值)
异步操作:
-
fs文件操作
require('fs').readFile('./index.html',(err,data)=>{});
-
数据库操作
-
AJAX
$.get('/server',(data)=>{});
-
定时器
setTimeout(()=>{},1000);
2.为什么要使用Promise
1.指定回调的方式更加灵活
- 旧方式:必须在启动异步任务前指定
- promise:启动异步任务 => 放回promise对象 => 给promise对象绑定回调函数(甚至可以在异步任务结束后指定/多个)
2.支持链式调用,可以解决回调地狱问题
回调地狱
1.什么是回到地狱?
回调函数嵌套调用,外部回调函数异步执行的结果是嵌套的回调执行的条件
示例:
const fs = require('fs');
fs.readfile('./resouce/1.html', (err, data1) => {
if (err) {
throw err;
}
fs.readfile('./resouce/2.html', (err, data2) => {
if (err) {
throw err;
fs.readfile('./resouce/3.html', (err, data3) => {
if (err) {
throw err;
console.log(data1 + data2 + data3);
}
});
}
});
});
2.回调地狱的缺点
- 不便于阅读
- 不便于异常处理
3.解决方案
promise链式调用
3.Promise初体验
1.体验1:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>promise初体验</title>
</head>
<body>
<div style="width: 80%;height: 30;text-align: center;">
<h1>promise初体验</h1>
<button id="btn">点击抽检</button>
</div>
<script>
function read(m, n) {
return Math.ceil(Math.random() * (m - m + 1)) + m - 1;
}
// //获取元素对象
// const btn = document.querySelector('#btn');
// //绑定事件
btn.addEventListener('click', () => {
// setTimeout(() => {
// //获取1到100的随机数
// let n = read(1, 100);
// if (n <= 30) {
// alert('恭喜你中奖了!');
// } else {
// alert('再接再厉!');
// }
// }, 1000);
//Promise 形式实现
const p = new Promise((resolve, reject) => {
setTimeout(() => {
let n = read(1, 100);
if (n <= 30) {
resolve();
} else {
reject();
}
}, 1000);
});
p.then(() => {
alert('恭喜你中奖了!');
}, () => {
alert('再接再厉!');
});
});
</script>
</body>
</html>
2.体验2(promise回调中获取成功失败值)
//Promise 形式实现
const p = new Promise((resolve, reject) => {
setTimeout(() => {
let n = read(1, 100);
if (n <= 30) {
resolve(n);
} else {
reject(n);
}
}, 1000);
});
p.then((value) => {
alert('恭喜你中奖了!' + value);
}, (reason) => {
alert('再接再厉!' + reason);
});
3.体验3
//Promise形式
const p = new Promise((resolve, reject) => {
//出错
if (err) {
reject();
}
reject();
});
p.then(value => {
console.log(value.toString());
}, reason => {
console.log(reason.toString());
});
4.体验4
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div>
<h2>Promise 封装 AJAX 操作</h2>
<button id="btn">点击发送Ajax</button>
</div>
<script>
const btn = document.querySelector('#btn');
btn.addEventListener('click', () => {
// // 1.创建对象
// const xhr = new XMLHttpRequest();
// //2.初始化
// xhr.open('GET', 'https://api.apipen.top/getJoke');
// // 3.发送
// xhr.send();
// // 4.处理响应结果
// xhr.onreadystatechange = () => {
// if (xhr.readyState === 4) {
// //判断响应状态码为2xxx
// if (xhr.status >= 200 && xhr.status < 300) {
// // 打印响应体
// console.log(xhr.response);
// } else {
// // 输出响应状态码
// console.log(xhr.status);
// }
// }
// }
// 使用promise
const p = new Promise((resolve, reject) => {
// 1.创建对象
const xhr = new XMLHttpRequest();
//2.初始化
xhr.open('GET', 'https://api.apipen.top/getJoke');
// 3.发送
xhr.send();
// 4.处理响应结果
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
//判断响应状态码为2xxx
if (xhr.status >= 200 && xhr.status < 300) {
// 打印响应体
resolve(xhr.response);
} else {
// 输出响应状态码
reject(xhr.status);
}
}
}
});
p.then(value => {
console.log(value);
}, reason => {
console.warn(reason);
});
});
</script>
</body>
</html>
5.封装练习
function mineReadFile(path) {
return new Promise((resolve, reject) => {
//读取文件
require('fs').readFile(path, (err, data) => {
//异常
if (err) {
reject(err);
}
resolve(data);
});
});
}
mineReadFile('./resource/content.txt')
.then(value => {
console.log(value.toString());
}, reason => {
console.warn(reason);
});
6.node.js中util.promisify方法
/**
* node.js中util.promisify 方法
* 解释:接收回调风格函数 返回promise
*/
//引入util模块
const util = require('util');
//引入fs模块
const fs = require('fs');
//返回新函数 promise
let mineReadFile = util.promisify(fs.readFile);
mineReadFile('./resource/content.txt')
.then(value => {
console.log(value.toString());
}, reason => {
console.warn(reason);
});
7.使用Promise封装ajax操作
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>使用promise封装Ajax请求</title>
</head>
<body>
<script>
// 1.封装一个函数sendAjax 发送 GET请求 参数:URL 返回结果:promise对象
function sedAJAX(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.open('GET', url);
//处理结果
xhr.onreadystatechange = function () {
//判断是否成功
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
//成功
resolve(xhr.response);
} else {
reject(xhr.status);
}
}
}
});
}
sedAJAX('https://api.apiopen.top/getJoke')
.then(value => {
console.log(value);
}, reason => {
console.warn(reason);
});
</script>
</body>
</html>
4.Promise对象
1.Promise状态
状态值改变
- pending变为resolved
- pending变为rejected
说明:只有这两种状态,且一个promise对象只能改变一次,无论变为成功还是失败,都会有一个结果数据,成功的结果数据一般定义为value,失败一般定义为reason
Promise的3种状态
实例对象的一个属性 【PromiseState】
-
pending 未决定的
-
resolved / fulfilled 成功
-
rejected 失败
2.Promise对象值
实例对象的另一个属性 【PromiseResult】
保存异步任务成功或失败的结果
3.Promise基本流程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CYYH7Eyj-1649561429782)(E:\笔记\image\image-20220403180323084.png)]
5.Promise使用
1.promise API
1.Promise构造函数:Promise(excutor){}
- executor函数:执行器(resolve,reject)=>{}
- resolve函数:内部定义成功时我们的调用函数 value => {}
- reject 函数:内部定义失败时我们调用的函数 reason=>{}
说明:executor会在Promise内部立即同步调用,异步操作在执行器中被执行
2.Promise.prototype.then 方法:(onResolved,onRejected)=>{}
- onResolved函数:成功的回调函数 (value)=>{}
- onrejected函数:失败的回调函数 (reason)=>{}
说明:指定用于得到成功value的成功回调和失败reason的失败回调,返回一个新的promise对象
3.Promise.prototype.catch方法:(onRejected) =>{}
- onRejected函数:失败的回调函数(reason)=>{}
说明:then()的语法糖,相当于:then(undefined,onRejected)
4.Promise.resolve方法:(value)=>{}
// 如果传入参数为非promise类型的对象,则返回成功的promise对象
// 如果传入的参数为promise对象,则返回结果为promise对象的结果
let p1 = Promise.resolve(521);
let p2 = Promise.reject(new Promise((resolve, reject) => {
resolve('ok');
}));
说明:返回一个成功或失败的promise对象
5.Promise.reject方法:(reason)=>{}
//不管返回类型是不是promise类型对象,结果都返回失败,如果是promise类型对象,则返回失败,失败结果为成功或失败的promise对象
let p3 = Promise.reject(new Promise((resolve, reject) => {
resolve('ok');
}));
说明:返回一个失败的promise对象
6.Promise.all方法:(promises)=>{}
- promises:包含n个promise的数组
说明:返回一个新的promise,只有所有的promise都成功才成功,只要有一个失败就直接失败
7.Promise.race 方法:(promises)=>{}
- promises:包含n个promise的数组
说明:返回一个新的promise,第一个完成的promise的结果状态就是最终的结果状态
8.改变promise对象状态的三种方法
- 调用resolve(), pending => fulfilled(resolved)
- 调用reject(),pending => rejected
- 抛出异常 throw ‘失败’;
9.Promise对象值改变和指定回调问题
- 当Promise对象中执行的是同步任务时,则先改变promise状态值,再指定回调,并执行回调函数
- 当Promise对象中执行的是异步任务时,先指定回调,再改变状态值,等状态值改变再执行回调函数
6.async函数
async function main() {
// async方法返回一个Promise对象
// 1.返回非promise类型参数时 方法结果为成功,成功结果为返回值
// return 520;
// 2.返回promise类型参数时,方法结果为改Promise对象结果
// return new Promise((resolve, reject) => {
// reject('失败!');
// });
//3.抛出异常,则结果为失败,失败结果为异常结果
throw '失败!';
};
console.log(main());
7.await表达式
- await右侧表达式一般为promise对象,但也可以是其他值
- 如果表达式是promise对象,await返回的是promise成功的值
- 如果表达式是其他值,直接将此值作为await的返回值
注意事项
- await必须写在async函数中,但async函数中可以没有await
- 如果await的promise失败了,就会抛出异常,需要通过try…catch捕获异常处理
async function main() {
let p1 = new Promise((resolve, reject) => {
resolve("成功了");
});
let p4 = new Promise((resolve, reject) => {
reject("失败了");
})
//1.右侧为Promise的情况,awai会返回promise对象成功的结果
//2.非Promise情况,则返回右侧原本结果
//失败的情况,需要使用try...catch捕获才能拿到失败结果
let p2 = await p1;
let p3 = await 10;
try {
let p5 = await p4;
} catch (e) {
console.log(e);
}
console.log(p2);
console.log(p3);
8.async和await对象结合实践
案例一:文件读取
// 需求:读取多个文件,拼接输出
// 回调方式:
const fs = require('fs');
fs.readfile('./resouce/1.html', (err, data1) => {
if (err) {
throw err;
}
fs.readfile('./resouce/2.html', (err, data2) => {
if (err) {
throw err;
fs.readfile('./resouce/3.html', (err, data3) => {
if (err) {
throw err;
console.log(data1 + data2 + data3);
}
});
}
});
});
const util = require('util');
const mineReadFile = util.promisify(fs.readfile);
// async与await
async function main() {
//1.获取文件1内容
try {
let data1 = await mineReadFile('./resource/1.html');
let data2 = await mineReadFile('./resource/2.html');
let data3 = await mineReadFile('./resource/3.html');
console.log(data1 + data2 + data3);
} catch (error) {
console.warn(error);
}
}
main();
案例二:ajax操作
// 1.封装一个函数sendAjax 发送 GET请求 参数:URL 返回结果:promise对象
function sedAJAX(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.open('GET', url);
//处理结果
xhr.onreadystatechange = function () {
//判断是否成功
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
//成功
resolve(xhr.response);
} else {
reject(xhr.status);
}
}
}
});
}
async function main() {
try {
let data = await sedAJAX('https://ap.apiopen.top/getJoke');
console.log(data);
} catch (error) {
console.log(error);
}
}