一、异步编程
以上四种都是通过回调函数来实现的
二、Promise 的优点
1. 支持链式调用,可以解决回调地狱问题
2. 指定回调函数的方式更加灵活
三、【案例】
1. 用回调函数,定时器
2. 用 Promise
当异步任务成功时调用 resolve,失败调用 reject
箭头函数
想要的得到随机数 n(得到上一个函数中的数据) ,可以使用Promise
代码
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>基本使用</title>
<link crossorigin='anonymous' href="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<h2 class="page-header">Promise 初体验</h2>
<button class="btn btn-primary" id="btn">点击抽奖</button>
</div>
<script>
//生成随机数
function rand(m,n){
return Math.ceil(Math.random() * (n-m+1)) + m-1;
}
/**
点击按钮, 1s 后显示是否中奖(30%概率中奖)
若中奖弹出 恭喜恭喜, 奖品为 10万 RMB 劳斯莱斯优惠券
若未中奖弹出 再接再厉
*/
//获取元素对象
const btn = document.querySelector('#btn');
//绑定单击事件
btn.addEventListener('click', function(){
//定时器
// setTimeout(() => {
// //30% 1-100 1 2 30
// //获取从1 - 100的一个随机数
// let n = rand(1, 100);
// //判断
// if(n <= 30){
// alert('恭喜恭喜, 奖品为 10万 RMB 劳斯莱斯优惠券');
// }else{
// alert('再接再厉');
// }
// }, 1000);
//Promise 形式实现
// resolve 解决 函数类型的数据
// reject 拒绝 函数类型的数据
const p = new Promise((resolve, reject) => {
setTimeout(() => {
//30% 1-100 1 2 30
//获取从1 - 100的一个随机数
let n = rand(1, 100);
//判断
if(n <= 30){
resolve(n); // 将 promise 对象的状态设置为 『成功』
}else{
reject(n); // 将 promise 对象的状态设置为 『失败』
}
}, 1000);
});
console.log(p);
//调用 then 方法
// value 值
// reason 理由
p.then((value) => {
alert('恭喜恭喜, 奖品为 10万 RMB 劳斯莱斯优惠券, 您的中奖数字为 ' + value);
}, (reason) => {
alert('再接再厉, 您的号码为 ' + reason);
});
});
</script>
</body>
</html>
四、用Promise封装文件
const fs = require('fs');
//回调函数 形式
// fs.readFile('./resource/content.txt', (err, data) => {
// // 如果出错 则抛出错误
// if(err) throw err;
// //输出文件内容
// console.log(data.toString());
// });
//将代码转为文字toString()
//Promise 形式
let p = new Promise((resolve , reject) => {
fs.readFile('./resource/content.tx', (err, data) => {
//如果出错
if(err) reject(err);
//如果成功
resolve(data);
});
});
//调用 then
p.then(value=>{
console.log(value.toString());
}, reason=>{
console.log(reason);
});
五、Promise封装AJAX
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Promise 封装 AJAX</title>
<link crossorigin='anonymous' href="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<h2 class="page-header">Promise 封装 AJAX 操作</h2>
<button class="btn btn-primary" id="btn">点击发送 AJAX</button>
</div>
<script>
//接口地址 https://api.apiopen.top/getJoke
//获取元素对象
const btn = document.querySelector('#btn');
btn.addEventListener('click', function(){
//创建 Promise
const p = new Promise((resolve, reject) => {
//1.创建对象
const xhr = new XMLHttpRequest();
//2. 初始化
xhr.open('GET', 'https://api.apiopen.top/getJoke');
//3. 发送
xhr.send();
//4. 处理响应结果
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
//判断响应状态码 2xx
if(xhr.status >= 200 && xhr.status < 300){
//控制台输出响应体
resolve(xhr.response);
}else{
//控制台输出响应状态码
reject(xhr.status);
}
}
}
});
//调用then方法
p.then(value=>{
console.log(value);
}, reason=>{
console.warn(reason);
});
});
</script>
</body>
</html>
异步代码
//1.创建对象 const xhr = new XMLHttpRequest(); //2. 初始化 给接口地址发送请求 xhr.open('GET', 'https://api.apiopen.top/getJoke'); //3. 发送 xhr.send(); //4. 处理响应结果 xhr.onreadystatechange = function(){ if(xhr.readyState === 4){ //判断响应状态码 2xx if(xhr.status >= 200 && xhr.status < 300){ //控制台输出响应体 resolve(xhr.response); }else{ //控制台输出响应状态码 reject(xhr.status); } } }
如果成功时调 resolve,失败时调 reject,再将其结果传进去,再用then,对结果进行处理(value成功的值,reason失败的值)
六、读取文件
/**
* 封装一个函数 mineReadFile 读取文件内容
* 参数: path 文件路径
* 返回: promise 对象
*/
//封装一个函数
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.log(reason);
});
直接进行读取,不用再写回调函数
七、util.promisify 方法
/**
* util.promisify 方法
*/
//引入 util 模块
const util = require('util');
//引入 fs 模块
const fs = require('fs');
//返回一个新的函数
let mineReadFile = util.promisify(fs.readFile);
mineReadFile('./resource/content.txt').then(value=>{
console.log(value.toString());
});
八、Promise 的状态
实例对象中的一个属性 【PromiseState】
1. pending 未决定的
2. resolved/fullfilled 成功
3. rejected失败
九、Promise 对象的值
实例对象中的另一个属性 【PromiseResult】
保存着异步任务【成功/失败】的结果
1. resolve
2. reject
十、Promise 的基本流程![](https://i-blog.csdnimg.cn/direct/3b90fd83e6c1447d805b2e8fe4a43fc4.png)
十一、API
catch 传入一个失败的回调,对结果进行处理(将报错变为不报错)
resolve
reject 传入的是一个失败的对象,但是,传入的值是什么,最后显示的值就是什么
十二、Promise中的几个关键问题
改变状态,都会执行
弹窗+控制台页面都会执行
不调用resolve,两个回调都不会执行
如果在异步回调中调用resolve,先指定回调,再改变方法
先指定回调的话,就是先改变状态,再调用回调函数,得到数据
第一个框会成功返回success
第二个框 会是undefined
要是失败的话,不会执行黄框中的内容,直接执行红色框中的内容
要是成功的话,就执行黄框中的内容,红框中的内容不显示
如果中间有一步失败,则最后的结果是红色框内容
如果出现这一种情况,则p结果还是p,p状态没有改变,则不会执行下面红框中的回调函数
所以最终结果只执行第一个,只打印 111
十三、Promise自定义封装
1. 初始结构
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Promise-封装 | 1 - 初始结构搭建</title>
<script src="./promise.js"></script>
</head>
<body>
<script>
let p = new Promise((resolve, reject) => {
resolve('OK');
});
p.then(value => {
console.log(value);
}, reason=>{
console.warn(reason);
})
</script>
</body>
</html>
js 框架
//声明构造函数
function Promise(executor){
}
//添加 then 方法
Promise.prototype.then = function(onResolved, onRejected){
}
2. resolve与reject结构搭建![](https://i-blog.csdnimg.cn/direct/2f76cd8bb2f4452ba163ea5ee5b038a9.png)
3. resolve与reject函数实现
4. throw抛出错误改变状态
5. 状态只能修改一次
6. then 方法执行回调
this 指向实例对象 p,this.PromiseState就是获取 p 身上的 PromiseState 属性
7. 异步任务then方法执行回调
保存回调函数
【代码】
//声明构造函数
function Promise(executor){
//添加属性
this.PromiseState = 'pending';
this.PromiseResult = null;
//声明属性
this.callback = {};
//保存实例对象的 this 的值
const self = this;// self _this that
//resolve 函数
function resolve(data){
//判断状态
if(self.PromiseState !== 'pending') return;
//1. 修改对象的状态 (promiseState)
self.PromiseState = 'fulfilled';// resolved
//2. 设置对象结果值 (promiseResult)
self.PromiseResult = data;
//调用成功的回调函数
if(self.callback.onResolved){
self.callback.onResolved(data);
}
}
//reject 函数
function reject(data){
//判断状态
if(self.PromiseState !== 'pending') return;
//1. 修改对象的状态 (promiseState)
self.PromiseState = 'rejected';//
//2. 设置对象结果值 (promiseResult)
self.PromiseResult = data;
//执行回调
if(self.callback.onResolved){
self.callback.onResolved(data);
}
}
try{
//同步调用『执行器函数』
executor(resolve, reject);
}catch(e){
//修改 promise 对象状态为『失败』
reject(e);
}
}
//添加 then 方法
Promise.prototype.then = function(onResolved, onRejected){
//调用回调函数 PromiseState
if(this.PromiseState === 'fulfilled'){
onResolved(this.PromiseResult);
}
if(this.PromiseState === 'rejected'){
onRejected(this.PromiseResult);
}
//判断 pending 状态
if(this.PromiseState === 'pending'){
//保存回调函数
this.callback = {
onResolved: onResolved,
onRejected: onRejected
}
}
}
8. 指定多个回调
①
变为
②
变为
③遍历数组 forEach
9. 同步修改状态then方法返回结果![](https://i-blog.csdnimg.cn/direct/c7f651850238480b9ec925418930c220.png)
10. 异步修改状态then方法返回结果
11. then方法代码优化(封装一个函数)
12. catch 方法与异常穿透
catch 指定失败的回调函数
① 实现catch方法运行
② 若不成功,每一步都出错,或者中间有一步出错。则抛出错误,到最后的catch 处理失败回调
③ 若成功则结果出现222,333
13. Promise.resolve封装
14. Promise.reject 封装
15. Promise.all 封装
都成功才可以成功
、
16. Promise.race 封装
谁状态先改变,结果就是谁的值
17. 回调函数「异步执行」
改变完状态后,这个函数还想要执行,就必须异步执行
运行结果应该是111,333,222
18. class版本封装
异步的时候,括号里面为空
【HTML代码】
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Promise-封装 | 17 - class版本封装</title>
<script src="./promise.js"></script>
</head>
<body>
<script>
// let p1 = new Promise((resolve, reject) => {
// setTimeout(() => {
// // resolve("OK");
// reject("Eror");
// })
// });
// p1.then(value => {
// console.log(value);
// }, reason => {
// console.warn(reason);
// });
console.log(Promise.resolve('OK'));
</script>
</body>
</html>
【JS代码】
class Promise{
//构造方法
constructor(executor){
//添加属性
this.PromiseState = 'pending';
this.PromiseResult = null;
//声明属性
this.callbacks = [];
//保存实例对象的 this 的值
const self = this;// self _this that
//resolve 函数
function resolve(data){
//判断状态
if(self.PromiseState !== 'pending') return;
//1. 修改对象的状态 (promiseState)
self.PromiseState = 'fulfilled';// resolved
//2. 设置对象结果值 (promiseResult)
self.PromiseResult = data;
//调用成功的回调函数
setTimeout(() => {
self.callbacks.forEach(item => {
item.onResolved(data);
});
});
}
//reject 函数
function reject(data){
//判断状态
if(self.PromiseState !== 'pending') return;
//1. 修改对象的状态 (promiseState)
self.PromiseState = 'rejected';//
//2. 设置对象结果值 (promiseResult)
self.PromiseResult = data;
//执行失败的回调
setTimeout(() => {
self.callbacks.forEach(item => {
item.onRejected(data);
});
});
}
try{
//同步调用『执行器函数』
executor(resolve, reject);
}catch(e){
//修改 promise 对象状态为『失败』
reject(e);
}
}
//then 方法封装
then(onResolved,onRejected){
const self = this;
//判断回调函数参数
if(typeof onRejected !== 'function'){
onRejected = reason => {
throw reason;
}
}
if(typeof onResolved !== 'function'){
onResolved = value => value;
//value => { return value};
}
return new Promise((resolve, reject) => {
//封装函数
function callback(type){
try{
//获取回调函数的执行结果
let result = type(self.PromiseResult);
//判断
if(result instanceof Promise){
//如果是 Promise 类型的对象
result.then(v => {
resolve(v);
}, r=>{
reject(r);
})
}else{
//结果的对象状态为『成功』
resolve(result);
}
}catch(e){
reject(e);
}
}
//调用回调函数 PromiseState
if(this.PromiseState === 'fulfilled'){
setTimeout(() => {
callback(onResolved);
});
}
if(this.PromiseState === 'rejected'){
setTimeout(() => {
callback(onRejected);
});
}
//判断 pending 状态
if(this.PromiseState === 'pending'){
//保存回调函数
this.callbacks.push({
onResolved: function(){
callback(onResolved);
},
onRejected: function(){
callback(onRejected);
}
});
}
})
}
//catch 方法
catch(onRejected){
return this.then(undefined, onRejected);
}
//添加 resolve 方法
static resolve(value){
//返回promise对象
return new Promise((resolve, reject) => {
if(value instanceof Promise){
value.then(v=>{
resolve(v);
}, r=>{
reject(r);
})
}else{
//状态设置为成功
resolve(value);
}
});
}
//添加 reject 方法
static reject(reason){
return new Promise((resolve, reject)=>{
reject(reason);
});
}
//添加 all 方法
static all(promises){
//返回结果为promise对象
return new Promise((resolve, reject) => {
//声明变量
let count = 0;
let arr = [];
//遍历
for(let i=0;i<promises.length;i++){
//
promises[i].then(v => {
//得知对象的状态是成功
//每个promise对象 都成功
count++;
//将当前promise对象成功的结果 存入到数组中
arr[i] = v;
//判断
if(count === promises.length){
//修改状态
resolve(arr);
}
}, r => {
reject(r);
});
}
});
}
//添加 race 方法
static race (promises){
return new Promise((resolve, reject) => {
for(let i=0;i<promises.length;i++){
promises[i].then(v => {
//修改返回对象的状态为 『成功』
resolve(v);
},r=>{
//修改返回对象的状态为 『失败』
reject(r);
})
}
});
}
}
十四、async 与await
1. async 函数
规则与 then 方法类似,若是非promise对象,则结果值为对应参数;如果是promise对象,则结果值是有参数的性质决定,参数成功,则为resolved ,结果为其参数值,反之为rejected
2. await 表达式
注意
3. async与await结合
使用回调函数的方式实现同时调用三个文件,并且将他们三个合在一起
4. async与await结合发送AJAX
好啦!本次的分享到这里就结束啦~~ 我们下次再见~~~