Promise
入门
1.Promise
介绍
1.1 理解
1.抽象表达
-
Promise
是一门新的技术(ES6
规范) -
Promise
是JS
中进行异常编程的新解决方案备注:旧方案是单纯使用回调函数【】
2.具体表达
- 从语法上来说:
Promise
是一个构造函数 - 从功能上来说:
Promise
对象用来封装一个异步操作并可以获取其成功/失败的结果值
1.2 promise
的状态改变
由于Promise
实例中有一个属性:PromiseState
有三种状态
pending 未决定的,初化状态
resolved / fullfilled 成功,如果成功后,则将状态修改成该状态
rejected 失败 如果失败后,则将状态修改成该状态
pending
变为resolved
pending
变为rejected
说明:只有这2种
,且一个promise
对象只能改变一次,无论变为成功还失败,都会有一个结果数据,成功的结果数据一般称为了 value
,失败的结果数据一般称为reason
1.3Promise
对象值
实例对象量的另一个属性PromiseResutl
,保存着是异步任务【成功/失败】的结果,这个状态只能使用resolve
或reject
两个对象进行修改
resolve
reject
1.4 promise
的执行流程
2.Promise
快速入门
案例:
/**点击按钮, 1s 后显示是否中奖(30%概率中奖)若中奖弹出 恭喜恭喜, 奖品为 10万 RMB 劳斯莱斯优惠券若未中奖弹出 再接再厉*/
传统方式一:
<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 劳斯莱斯优惠券若未中奖弹出 再接再厉*/// 方式一:使用传统方式//获取元素对象let bnt = document.getElementById("btn");// 使用传统方式一;// 绑定单击事件bnt.onclick = ()=>{let result = rand(1,100);// 使用定时器 1s 后开奖setTimeout(()=>{if(result<=30){alert("恭喜恭喜, 奖品为 10万 RMB 劳斯莱斯优惠券");}else{alert("再接再厉");}},1000);};</script>
</body>
Promise
方式二:
// 使用Promise方式二;// 绑定单击事件bnt.onclick = ()=>{// 由于Promise是构造函数,所以可以直接new对象// 在Promise需要接收一个函数// 在这个函数中需要两个参数:resolve 和 reject 这个参数都是 `函数类型的数据`// let p = new Promise((resolve,reject)=>{setTimeout(() => {//30% 1-100 1 2 30//获取从1 - 100的一个随机数let n = rand(1, 100);//判断if(n <= 30){resolve(); // 将 promise 对象的状态设置为 『成功』}else{reject(); // 将 promise 对象的状态设置为 『失败』}}, 1000);});
// 对回调的结果进行处理// then需要接收两个回调函数// 第一个参数:成功后调用// 第二个参数:失败后调用p.then(()=>{alert("恭喜恭喜, 奖品为 10万 RMB 劳斯莱斯优惠券");},()=>{alert("再接再厉");});
<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 劳斯莱斯优惠券若未中奖弹出 再接再厉*/// 方式一:使用传统方式//获取元素对象let bnt = document.getElementById("btn");// 使用Promise方式二;// 绑定单击事件bnt.onclick = ()=>{// 由于Promise是构造函数,所以可以直接new对象// 在Promise需要接收一个函数// 在这个函数中需要两个参数:resolve 和 reject 这个参数都是 `函数类型的数据`// let 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);});// 对回调的结果进行处理// then需要接收两个回调函数// 第一个参数:成功后调用// 第二个参数:失败后调用p.then((value)=>{alert("恭喜恭喜, 奖品为 10万 RMB 劳斯莱斯优惠券,您的中奖数字为"+value);},(reason)=>{alert("再接再厉,您的号码为"+reason);});};</script>
</body>
3.fs
读取文件
使用传统回调函数方式:
// 回调用函数形式
const fs = require('fs');
fs.readFile('./resource/content.txt',(err,data)=>{// 如果出现异常,抛出if(err){throw err;}// 没有异常则打印console.log(data.toString);
});
使用Promise
方式:
// 第一步:创建 Promise对象
const p = new Promise((resolve,reject)=>{// 第二步:读取文件fs.readFile('./resource/content.txt',(error,data)=>{if(data){resolve(data);}if(error){reject(error);}});
});p.then((data)=>{console.log(data.toString);},(err)=>{throw err;}
);
4.Promise
实践练习AJAX
请求
传统方式:
<body><div class="container"><h2 class="page-header">Promise 封装 AJAX 操作</h2><button class="btn btn-primary" id="btn">点击发送 AJAX</button></div><script>//接口地址 https://fanyi.baidu.com///获取元素对象const btn = document.querySelector('#btn');// btn绑定单击事件btn.onclick = ()=>{// 创建对象let xhr = new XMLHttpRequest();// 初化urlxhr.open("get","https://fanyi.baidu.com/");// 发送xhr.send();// 对响应结果处理xhr.onreadystatechange = ()=>{// 判断是否完成响应if(xhr.readyState === 4){if(xhr.status>=200&&xhr.status<300){alert(xhr.response);console.log(xhr.response);}else{alert(xhr.status);}}};}</script>
</body>
Promise
方式
<body><div class="container"><h2 class="page-header">Promise 封装 AJAX 操作</h2><button class="btn btn-primary" id="btn">点击发送 AJAX</button></div><script>//接口地址 https://fanyi.baidu.com///获取元素对象const btn = document.querySelector('#btn');btn.onclick = ()=>{// 创建对象let p = new Promise((resolve,reject)=>{let xhr = new XMLHttpRequest();// 初化urlxhr.open("get","https://api.apiopen.top/getJoke");// 发送xhr.send();// 判断是否成功响应xhr.onreadystatechange = ()=>{if(xhr.readyState===4){if(xhr.status>=200&&xhr.status<300){resolve(xhr.response);}else{reject(xhr.status);}}}});// 处理promise的结果p.then((value)=>{console.log(value);},(reason)=>{console.log(reason);});};</script>
</body>
<!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(){//创建 Promiseconst 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>
5.Promise
封装练习-fs模块
案例:
/*** 封装一个函数 mineReadFile 读取文件内容* 参数: path 文件路径* 返回: promise 对象,如果成功resovle,失败返回reject*/
/*** 封装一个函数 mineReadFile 读取文件内容* 参数: path 文件路径* 返回: promise 对象,如果成功resovle,失败返回reject*/// 引入fs对象
const fs = require('fs');// 封装函数 mineReadFile
function mineReadFile(path){return new Promise((resovle,reject)=>{fs.readFile(path,(error,data)=>{if(error){reject(error);}resovle(data);})});
};// 调用函数测试
mineReadFile("./resource/content.txt").then((data)=>{console.log(data);},(error)=>{console.log(error);});
6.util.promisify
方法
/*** util.promisify 方法*/// 引入模块
const util = require("util");
const fs = require("fs");// 返回一个新的函数对象
let mineReadFile = util.promisify(fs.readFile);// 调用函数测试
mineReadFile("./resource/content.txt").then((data)=>{console.log(data.toString());},(error)=>{console.log(error);});
7.Promise
封装AJAX
请求
/*** 封装一个函数 sendAJAX 发送 GET AJAX 请求* 参数 URL* 返回结果 Promise 对象*/
<!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>
</head>
<body><script>/*** 封装一个函数 sendAJAX 发送 GET AJAX 请求* 参数 URL* 返回结果 Promise 对象*/function sendAJAX(url){return new Promise((resolve, reject) => {const xhr = new XMLHttpRequest();xhr.responseType = 'json';xhr.open("GET", url);xhr.send();//处理结果xhr.onreadystatechange = function(){if(xhr.readyState === 4){//判断成功if(xhr.status >= 200 && xhr.status < 300){//成功的结果resolve(xhr.response);}else{reject(xhr.status);}}}});}sendAJAX('https://api.apiopen.top/getJok').then(value => {console.log(value);}, reason => {console.warn(reason);});</script>
</body>
</html>
Promise
的API
1.Promise
构造函数:Promise(excutor){}
executor
函数:执行器(resolve,reject)=>{}
resolve
函数:内部定义成功时我们调用的函数value=>{}
reject
函数:内部定义失败时我们调用的函数reason=>{}
说明:executor
会在Promise
内部立即同步调用,异常操作在执行器中执行【就是说:传入的箭头函数是立即执行的】
2.Promise.prototype.then
方法:(onResolved,onRejected)=>{}
onResolved
函数:成功的回调函数(value)=>{}
onReject
函数:失败的回调函数(reason)=>{}
说明:指定用于得到成功value
的成功回调和用于得到失败reason
的失败回调返回一个新的promise
对象
3.Promise.prototype.catch
方法:(onRejected)=>{}
onRejected
函数:失败的回调函数(reason)=>{}
then()
语法糖,相当于then(undefiend,onRejected);
说明:注意是:catch
方法只能在失败时调用
注意是:then
和catch
方法都在Promise
对象调用的【也就是通过Promise
构造函数创建的对象,对象名调用】,不能直接通过Promise.
的形式调用
<script>let p = new Promise((resovle,reject)=>{/*** 我们可以通过rsovle或reject对象来修改是否成功或失败*/ // resovle("海康");//设置成功reject("error");//设置失败});// 对p结果处理// p.then(// (data)=>{// console.log(data);// },// (error)=>{// console.log(error);// }// )// 注意是:catch方法只能处理失败后的结果 p.catch((error)=>{console.log(error);});</script>
4. Promise.resolve
方法:(value)=>{}
value
:成功的数据或promise
对象
说明:返回一个成功/失败的promise
对象
注意是:resolve
方法是可以直接通过Promise.resolve("参数")
调用的,并且可以传入参数,如果传入参数是非Promise
对象返回就是成功,如果传入参数是Promise
对象,传入的Promise
对象返回成功就是成功,返回失败就是失败
<script>// Promise.resolve("参数")方法/*** 1.如果传入的参数为 非Promise类型的对象,则返回结果为成功的Promise对象。例如:传入 数值 布尔 等【除了Promse对象】* * 2.如果传入的参数为 Promise类型的对象,则返回结果由传入的Promise对象决定,如果传入的Promise返回的结果是成功,则成功,如果传入的Promise返回结果是失败,返回的结果就是失败*/ let p = Promise.resolve("海康");// 返回结果为:成功let p2 = Promise.resolve(new Promise((resovle,reject)=>{/*** 可以使用 resovle和reject函数对象修改是否成功或失败*/// resovle("湛江");reject("error");}));// 对象p2结果进行处理p2.then((data)=>{console.log(data);},(error)=>{console.log(error);});</script>
5.Promise.reject
方法:(reason)=>{}
reason
:失败的原因
说明:返回一个失败的promise
对象,不管传入的参数是什么永远返回的Promise
结果都是失败的
<script>// 在reject函数中不管传入什么参数,返回的结果永远都失败的let p = Promise.reject("永远都是失败的reject函数");p.catch((error)=>{console.log(error);});</script>
6.Promise.all
方法:(promise)=>{}
promise
:包含n
个promise
的数组
说明:返回一个新的promise
,只有所有的promise
都成功才成功,只要有一个失败了就直接失败,注意是:返回所有成功promise
对象组成的数组,失败就是直接返回失败的那个promise
对象
<script>// 定义几个Promise对象let p1 = new Promise((resolve,reject)=>{resolve("湛江");});let p2 = Promise.resolve("海康");let p3 = Promise.resolve("南宁");/*** 1.由于三个返回的结果都是成功,所以三者组成的数组返回,总结:成功组成数组全部返回* 2.如果其中的 p2 返回的结果是失败,则直接返回 p2 的结果*/ let result = Promise.all([p1,p2,p3]);
</script>
7.Promise.race
方法:(promise)=>{}
promise
:包含n
个promise
的数组
说明:返回一个新的promse
,第一个完成的promise
的结果状态就是最终的结果状态
Promise.reace
方法中的那个promise
对象状态先改变,返回结果就是那个对象的状态,如果那个对象返回成功,则成功,返回失败,则失败
<script>// 定义几个Promise对象let p1 = new Promise((resolve,reject)=>{setTimeout(()=>{resolve("湛江");},1000);});let p2 = Promise.resolve("海康");let p3 = Promise.resolve("南宁");/*** race返回的结果就是由于那个一个最先改状态的那个Promise对象,* 如果在这个案例中,是第二个先修改状态,所以返回就是第二个,由于返回成功,则成功,如果返回失败,则失败*/ let result = Promise.race([p1,p2,p3]);
</script>
注意是:resolve()
reject()
all()
race()
这四个方法都可以通过Promise.
的方式来调用的
async
和await
1.asyn
函数
- 函数的返回值为
promise
对象 promise
对象的结果由async
函数执行的返回值决定- 如果返回值是非
Promise
对象,则返回就会成功的Promise
对象 - 如果返回值是
Promise
对象,则Promise
返回成功还是失败,由该Promise
成功还是失败决定 - 如果抛出异常,则直接返回失败的
Promise
对象
- 如果返回值是非
asyn
和then
及Promise.resolve()
函数返回值一样的规则
<script>// async函数async function main(){// 1.如果直接返回一个 非Promise对象【如布尔 数值 字符串 null undefiend】,则async函数返回值就是一个成功的promise对象return "海康"; // 返回是成功的// 2.如果返回值是:Promise对象,成功还是失败,由该Promise对象决定return new Promise((resolve,reject)=>{// 返回失败,则失败// reject("error");// 返回成功,则成功resolve("success");});// 3.抛出异常,返回Promise对象是失败的throw "失败的Promise";}let result = main();alert(result);</script>
2. await
表达式
await
右侧的表达式一般为promise
对象,但也可以是其它的值- 如果表达式是
promise
对象,并且promise
是成功,await
返回的是promise
成功的值【是获取成功后传入的参数值,如果传入是success
,则返回就是success
】 - 如果表达式是
promise
对象,并且promise
是失败,我们需要使用try-catch
来捕获await
语句 - 如果表达式是其它值,直接将此值作为
await
的返回值
注意:
await
必须写在async
函数中,但async
函数中可以没有await
- 如果
await
的promise
失败了,就会抛出异常,需要通过try-catch
捕获处理
<script>// 注意是:await必须在asyncasync function main(){// 1.await右侧是 非Promies对象,await会将该值当返回值返回let res1 = await "海康";console.log(res1);// 2.await右侧是 Promise对象,如果 Promise对象是成功的,则返回成功的值let p = new Promise((resolve,reject)=>{resolve("success");});let res2 = await p;console.log(res2);console.log(typeof res2);// 3.await右侧是 Promise对象,如果 Promise对象是失败的,则需要`try-catch`来对`await`语句进行捕获let p2 = new Promise((resolve,reject)=>{reject("error");});try{let res3 = await p2;}catch(e){console.log(e);}}main();</script>
3.asyn
函数与await
表达式
注意是:util
中的promisify(参数)
是可以将一些API
转换成promise
对象,如:util.promisify(fs.readFile);
使用asyn
函数与await
表达式读取三个文件中的内容,并且拼接在一定输出
const fs = require("fs");
const util = require("util");
const mineReadFile = util.promisify(fs.readFile);async function main(){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);
}main();
4.async
与await
结合发送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>Document</title>
</head>
<body><button>获取段子了...</button><script>// 封装 ajax function sendAJAX(url){return new Promise((resovle,reject)=>{// 创建XMLHttpRequestlet xhr = new XMLHttpRequest();xhr.open('get',url);xhr.send();xhr.onreadystatechange = ()=>{if(xhr.readyState===4){if(xhr.status>=200&&xhr.status<300){resovle(xhr.response);}else{reject(xhr.status);}}}});}// 使用async与await结合发送ajax请求// 获取button对象let bnt = document.getElementsByTagName("button")[0];// 绑定单击事件//段子接口地址 https://api.apiopen.top/getJokebnt.onclick = async function(){alert("fdsfs");let result = await sendAJAX("https://api.apiopen.top/getJoke");console.log(result);}</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>async与await结合发送AJAX</title>
</head>
<body><button id="btn">点击获取段子</button><script>//axiosfunction sendAJAX(url){return new Promise((resolve, reject) => {const xhr = new XMLHttpRequest();xhr.responseType = 'json';xhr.open("GET", url);xhr.send();//处理结果xhr.onreadystatechange = function(){if(xhr.readyState === 4){//判断成功if(xhr.status >= 200 && xhr.status < 300){//成功的结果resolve(xhr.response);}else{reject(xhr.status);}}}});}//段子接口地址 https://api.apiopen.top/getJokelet btn = document.querySelector('#btn');btn.addEventListener('click',async function(){//获取段子信息let duanzi = await sendAJAX('https://api.apiopen.top/getJoke');console.log(duanzi);});</script>
</body>
</html>