目录
一:
Promise是什么?
二:为什么要是用Promise?
三:Promise体验(随机数抽奖小案例)
四:Promise实践练习
五:Promise的状态改变
六:Promise的基本流程
七:如何使用Promise?
八:Promise的几个关键问题
一:
Promise是什么?
1. 抽象表达:
1):Promise是一门新的技术(ES6)规范
2):Promise是JS中进行异步编程的新解决方案(备注:旧的解决方案是单纯使用回调函数)
2. 具体表达:
1):从语法上来说:Promise是一个构造函数
2):从功能上来说:Promise对象用来封装一个异步操作并可以获取其成功/失败的结果值
扩展:异步编程(包括但不限于以下)
-
fs :文件操作(node.js下面的一个模块,对计算机磁盘进行读写操作)
require('fs').readFile('./index.html',(err,data)=>{})
- 数据库操作(例如:MySQL)
-
AJAX
$.get('/server',(data)=>{})
-
定时器
setTimeout(()=>{},time)
二:为什么要是用Promise?
1.指定回到函数的方式更灵活
- 1):旧的:必须在启动异步任务前指定
- 2):Promise:启动异步任务===》返回Promise对象===》给Promise对象绑定回调函数(甚至可以在异步任务结束后指定/多个)
2.支持链式调用,可以解决回调地域问题
1):什么是回调地狱?
-
回调函数嵌套调用,外部回调函数异步执行的结果是嵌套的回调执行的条件
asyncFunc1(opt,(...args1)=>{ asyncFunc2(opt,(...args2)=>{ asyncFunc3(opt,(...args3)=>{ asyncFunc4(opt,(...args4)=>{ //some operation }) }) }) })
2):回调地狱的缺点?
- 不便于阅读
- 不便于异常处理
3):解决方案?
- Promise链式调用
4):终极解决方案?
- async / await
三:抽奖小案例
<body>
<button id="btn">点击抽奖</button>
<script>
//生成随机数
function rand(m, n) {
return Math.ceil(Math.random() * (n - m + 1)) + m - 1;
}
/*
点击按钮 ,2s后显示是否中奖(30%概率中奖)
若中奖弹出:恭喜恭喜,奖品为10万RMB劳斯莱斯优惠券
若未中奖弹出:再接再厉
*/
//获取元素对象
const btn = document.getElementById("btn");
// 绑定单击事件
btn.onclick=function () {
//30%中奖概率
// setTimeout(() => {
// let num = rand(1, 100);
// if (num <= 30) {
// alert("恭喜恭喜,奖品为10万RMB劳斯莱斯优惠券"+',中奖号码为'+num);
// } else {
// alert('再接再厉'+',您的号码为'+num);
// }
// }, 1000);
//Promise形式实现
//resolve 解决 (函数类型的数据)
// reject 拒绝 (函数类型的数据)
const P = new Promise((resolve, reject) => {
setTimeout(() => {
let num = rand(1, 100);
if (num <= 30) {
resolve(num);//调完之后可以将Promise对象(P)的状态设置为【成功】
} else {
reject(num);//调完之后可以将Promise对象(P)的状态设置为【失败】
}
}, 1000);
});
//调用then方法
P.then(
(value) => {
alert("恭喜恭喜,奖品为10万RMB劳斯莱斯优惠券,您的中奖号码为:"+value);
},
(reason) => {
alert("再接再厉,您的号码为:"+reason);
}
);
};
</script>
</body>
四:实践练习
-
fs基础(在node.js环境下运行;例如:node xxx.js(就是下方代码所在的js文件))
const fs = require("fs"); // 回调函数形式 // fs.readFile('./resource/content.txt',(err,data)=>{ // // 如果出错就抛出错误 // if(err) throw error; // // 输出文件内容(BUffer类型) // console.log(data); // // 输出文件内容(转为字符串) // console.log(data.toString()); // }) // Promise 形式 const P = new Promise((resolve, reject) => { fs.readFile("./resource/content.txt", (err, data) => { if (err) reject(err); resolve(data); }); }).then( (value) => { console.log(value.toString()); }, (reason) => { console.log(reason); } );
-
ajax请求
<body> <button id="btn">点击发送AJAX</button> <script> // 接口地址:https://api.apiopen.top/getJoke const btn = document.getElementById("btn"); btn.onclick = function () { //创建Promise const P = new Promise((resolve, reject) => { // 创建对象 let xhr = new XMLHttpRequest(); //设置返回数据为json格式 xhr.responseType='json' // 初始化 xhr.open("GET", "https://api.apiopen.top/getJoke"); // 处理响应 xhr.onreadystatechange = function () { if (xhr.readyState === 4) { // 5.判断响应状态码 if (xhr.status >= 200 && xhr.status < 300) { resolve(xhr.response); } else { reject(xhr.status); } } }; // 发送 xhr.send(); }).then( (value) => { console.log(value); }, (reason) => { console.warn(reason); } ); // 创建对象 // let xhr = new XMLHttpRequest(); //设置返回数据为json格式 xhr.responseType='json' // // 初始化 // xhr.open("GET", "https://api.apiopen.top/getJoke"); // // 处理响应 // xhr.onreadystatechange = function () { // if (xhr.readyState === 4) { // // 5.判断响应状态码 // if (xhr.status >= 200 && xhr.status < 300) { // //控制台输出响应体 // console.log(xhr.response); // } else { // //控制台输出错误的响应码 // console.log(xhr.status); // } // } // }; // // 发送 // xhr.send(); }; </script> </body>
3. 封装fs文件(mineReadFile)
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); } );
4. util.promisify(original) (node.js下面的一个模块,传入一个遵循常见的错误优先的回调风格的函数)
// 引入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()); })
5.封装AJAX
function sendAJAX(url) { return new Promise((resolve, reject) => { let xhr = new XMLHttpRequest(); xhr.responseType='json' xhr.open("GET", "https://api.apiopen.top/getJoke"); xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status >= 200 && xhr.status < 300) { resolve(xhr.response); } else { reject(xhr.status); } } }; xhr.send(); }); } sendAJAX("https://api.apiopen.top/getJoke") .then(value => {console.log(value);},(reason) => {console.warn(reason);});
五:
1.promise对象状态属性介绍【promiseState】
- pending变为resolved/fulfilled(成功)
- pending变为rejected(失败 )
说明:只有这两种,且一个promise对象只能改变一次;无论变为成功还是失败,都会有一个结果数据;成功的结果数据一般为value,失败的结果数据一般称为reason
2. promise对象结果值属性介绍【promiseResult】
- 保存的是异步任务:对象[ 成功 / 失败 ] 的结果
- 只有resolve()和reject() 可以对其改变赋值
六:Promise的基本流程
七:如何使用Promise?
一. API
1.Promise构造函数:Promise(excutor){}
(1): excutor函数:执行器 (resolve,reject)=>{ };
(2):resolve函数:内部定义成功时我们调用的函数 value =>{ };
(3):reject函数:内部定义成功时我们调用的函数 reason=>{ };
说明:excutor会在Promise内部立即同步调用,异步操作在执行器中执行。
let P = new Promise((resolve,reject)=>{
//同步执行
console.log('11111')
})
console.log('22222')
//打印结果:
//11111
//22222
2. Promise.prototype.then方法:(onResolved,onRejected)=>{ }
(1):onResolved函数:成功的回调函数 (value)=>{ };
(2): onRejected函数:失败的回调函数 (reason)=>{ };
说明:指定用于得到成功:value的成功回调和用于得到失败:reason的失败回调,返回一个新的promise对象
3. Promise.prototype.catch 方法:(onRejected)=>{ }
(1):onRejected函数:失败的回调函数 (reason)=>{ };
let P = new Promise((resolve, reject) => {
resolve("error");
});
P.catch((reason) => {
console.log(reason);//error
});
4. Promise.resolve 方法:(value)=>{ };
(1):value:成功的数据 或 promise对象
说明:返回一个成功/失败的promise对象
let P1 = Promise.resolve("promise");
//如传入的参数为 非promise类型的对象,则返回的结果为成功promise对象
console.log(P1);//返回的结果如下图 (左边)
let P2 = Promise.resolve(new Promise((resolve,reject)=>{
resolve('OK')
}));
//如传入的参数为promise对象,则参数的结果决定了resolve的结果
console.log(P2);//返回的结果如下图 (右边)
5. Promise.reject 方法:(reason)=>{ };
(1):reason:失败的原因
说明:返回一个失败的promise对象
let P = Promise.reject(996);
let P2 = Promise.reject("promise");
let P3 = Promise.reject((resolve, reject) => {
resolve("OK");
});
//无论传入什么,结果都是失败
console.log(P,P2,P3); //结果如下图
6. Promise.all 方法:(promise)=>{ };
(1):promise:包含n个promise的数组
说明:返回一个新的promise,只有所有的promise都成功才成功,只要有一个失败了就直接失败
//全部成功
let P = new Promise((resolve, reject) => {
resolve("OK");
});
let P2 = Promise.resolve("success");
let P3 = Promise.resolve("Yes");
const result = Promise.all([P, P2, P3]);
console.log(result);//全部成功的结果如下图(左边)
//有失败
let P = new Promise((resolve, reject) => {
resolve("OK");
});
let P2 = Promise.resolve("success");
let P3 = Promise.resolve("Yes");
let P4 = Promise.reject("error");
const result = Promise.all([P, P2, P3,P4]);
console.log(result);//有失败的结果如下图(右边)
7. Promise.race方法:(promise)=>{ };
(1):promise:包含n个promise的数组
说明:返回一个新的promise,第一个完成的promise的结果状态就是最终的结果状态
let P = new Promise((resolve, reject) => {
resolve("OK");
});
let P2 = Promise.resolve("success");
let P4 = Promise.reject("error");
const result = Promise.race([P, P2, P3, P4]);
console.log(result); // OK
let P = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("OK");
}, 100);
});
let P2 = Promise.resolve("success");
let P4 = Promise.reject("error");
const result = Promise.race([P, P2, P3, P4]);
console.log(result); //success
八:Promise的几个关键问题
1. 如何改变promise的状态?
(1):resolve(value):如果当前是pending就会变为resolved;
(2):reject(reason):如果当前是pending就会变为rejected;
(3):抛出异常:如果当前是pending就会变为rejected
let P = new Promise((resolve ,reject)=>{
// 1.resolve函数
// resolve('OK') //pending ===> resolved (fulfilled)
// 2.reject函数
// reject('error') //pending ===> rejected
// 3.抛出异常
// throw '阿欧,出问题了'
})
2. 一个promise指定多个成功/失败回调函数,都会调用吗?
当promise改变为对应状态时都会调用
3. 改变promise状态【resolve()/reject()】和指定回调函数【then()】谁先谁后?
(1):都有可能,正常情况下是先指定回调再改变状态,但也可以先改变状态再指定回调
(2):如何先改变状态再指定回调?
①:在执行器中直接调用resolve() /reject()===>{同步任务}
②:延迟更长时间才调用then()
(3):什么时候才能得到数据?
①:如果先指定的回调,那当状态发生改变时,回调函数就会调用,得到数据
②:如果先改变的状态,那当指定回调时,回调函数就会调用,得到数据
4. promise.then() 返回新的promise的结果状态有什么决定?
(1):简单表达:有then()指定的回调函数执行的结果决定
(2):详细表达
①:如果抛出异常,新promise变为rejected,reason为抛出的异常
②:如果返回的是非promise的任意值,新promise变为resolved,value为返回值
③:如果返回的是另一个新promise,此promise的结果就会成为新promise的结果
5. promise如何串联多个操作任务?
(1):promise的then()返回一个新的promise,可以看成then()的链式调用
(2):通过then的链式调用串联多个同步/异步任务
let P = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("OK");
}, 1000);
}).then((value) => {
return new Promise((resolve, reject) => {
resolve("success");
});
}).then((value) => {
console.log(value); // success
}).then((value) => {
console.log(value); //undefined
});
6. promise异常穿透?
(1):当使用promise的then链式调用时,可以在最后指定失败的回调
(2):前面任何操作出了异常,都会传到最后失败的回调中处理
let P = new Promise((resolve, reject) => {
setTimeout(() => {
reject("error");
}, 1000);
}) .then((value) => {
return new Promise((resolve, reject) => {
resolve("success");
});
}) .then((value) => {
console.log(111);
}) .then((value) => {
console.log(222);
}).catch((reason) => {
console.warn(reason); //error
});
//例子:中间某一个环节错误
let P = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("OK");
}, 1000);
}) .then((value) => {
return new Promise((resolve, reject) => {
resolve("success");
});
}) .then((value) => {
throw '失败啦!'
}) .then((value) => {
console.log(222);
}).catch((reason) => {
console.warn(reason); //error
});
7. 中断promise链?
(1):当使用promise的then链式调用时,在中间中断,不再调用后面的回调函数
(2):办法:在回调函数中返回一个pending状态的promise对象
let P = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("OK");
}, 1000);
})
.then((value) => {
console.log(111);
// 有且只有一个方式
return new Promise(() => {});
})
.then((value) => {
console.log(222);
})
.then((value) => {
console.log(333);
})
.catch((reason) => {
console.warn(reason); //error
});
//最后控制台打印结果为:111