一.promise是什么?
1.promise理解
抽象表达:promise是一门新的技术(ES6规范),是js中进行异步编程的新解决方案(旧方案是使用回调函数)
异步编程种类:fs文件操作,AJAX,定时器
具体表达:从语法上说是一个构造函数,从功能上说promise对象用来封装一个异步操作并可以取得成功/失败的结果值
2使用promise的优点
指定回调函数的方式更加灵活(promise:启动异步任务=>返回promise对象=>给promise对象绑定回调函数)
3.promise初体验(用promise做一个类似于抽奖的东西)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>1.Promise的初体验</title>
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.6.1/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 劳斯莱斯优惠券
若未中奖弹出 再接再厉
*/
var button = document.querySelector("button");
button.addEventListener('click',function(){
//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)
})
//调用then方法
//value 值
//reason 理由
p.then((value)=>{
alert("恭喜恭喜, 奖品为 10万 RMB 劳斯莱斯优惠券,你的中奖数字为"+value);
},(reson)=>{
alert("再接再厉,您的号码为"+reson);
})
})
</script>
</body>
</html>
4.Promise实践练习-fs读取文件
const fs = require('fs');
// fs.readFile('./resource/content.txt',(err,data)=>{
// //如果出错,则抛出错误
// if(err) throw err;
// //输出文件内容
// console.log(data.toString());
// })
//Promise形式
//resolve 解决 函数类型的数据
//reject 拒绝 函数类型的数据
let p = new Promise((resolve, reject)=>{
fs.readFile('./resource/content.txt',(err,data)=>{
//如果出错
if(err){reject(err)}
//如果成功
resolve(data);
})
})
p.then(value=>{
console.log(value.toString());
},reason=>{
console.log(reason);
})
5.Promise实践练习-AJAX请求
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>3.Promise实践练习-Ajax请求</title>
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.6.1/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(){
// //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)
// {
// //控制台输出响应体
// console.log(xhr.response);
// }
// else{
// //控制台输出响应状态码
// console.log(xhr.status);
// }
// }
// }
// })
const btn = document.querySelector('#btn');
btn.addEventListener('click',function(){
//创建Promise
const p = new Promise((resolve,reject)=>{
const xhr = new XMLHttpRequest();
xhr.open('GET','https://api.apiopen.top/getJoke');
xhr.send();
xhr.onreadystatechange=function(){
if(xhr.readyState === 4)
{
if(xhr.status>=200 && xhr.status<300)
{
resolve(xhr.response);
}
else{
reject(xhr.status);
}
}
}
})
p.then((value)=>{
console.log(value);
},(reason)=>{
console.log(reason);
})
})
</script>
</body>
</html>
6.Promise封装fs读取文件操作
/**
* 封装一个函数 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);
});
7.util.promisify方法进行promise风格转化
//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());
},(reason)=>{
console.log(reason);
})
8.Promise封装AJAX请求
/*封装一个函数 sendAJAX 发送 GET AJAX 请求
参数 URL
返回结果 Promise对象
*/
function sendAJAX(url){
return new Promise((resolve,reject)=>{
const xhr = new XMLHttpRequest();
xhr.responseText = '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/getJoke').then(value=>{
console.log(value);
},reason=>{
console.warn(reason);
})
二.promise的状态以及对象的值
1.promise的三种状态
promise的状态其实是实例对象中的一个内置属性(PromiseState),所以不能够对该属性做直接的操作
- 1.pending未定的状态
- 2.fulfilled/resolved
- 3.rejected
2.promise的状态的改变
只有两种情况,且一个promise对象只能改变一次,不能由成功变失败,也不能失败变成功
- pending->resolved
- pending->rejected
成功的结果数据一般称为value,失败的结果一般称为reason
3.promise对象的值——实例对象中的另外一个属性promiseResult
该值保存了异步任务成功/失败的结果
其结果由resolve和reject执行获得
三.如何使用Promise
1.API
1.1Promise的构造函数:Promise(excutor){}
- excutor函数:执行器(resolve,reject)=>{}
- resolve函数:内部定义成功时我们调用的函数value=>{}
- reject函数:内部定义失败时我们调用的函数reason=>{}
说明:executor会在promise内部立即同步调用,异步操作在执行器中执行
let p=new Promise((resolve,reject)=>{
console.log(111);
})
console.log(222);
//输出结果为:
//111
//222
1.2Promise.prototype.then方法:(onResolved,onRejected)=>{}
- onResolved函数:成功的回调函数(value)=>{}
- onRejected函数:失败的回调函数(reason)=>{}
1.3Promise.prototype.catch方法:(onRejected)=>{}
onRejected函数:失败的回调函数(reason)=>{}
let p = new Promise((resolve,reject)=>{
reject('error')
});
//执行catch方法
p.catch(reason=>{
console.log(reason);//error
})
2.promise的几个关键问题
2.11promise.resolve方法:(value)=>{}
如果传入的参数为非Promised类型的对象,则返回的结果为成功的Promise
传入的参数为Promise对象,则参数的结果决定了resolve的结果
//如果传入的参数为非Promised类型的对象,则返回的结果为成功的Promise
let p1 = Promise.resolve('521');
console.log(p1);
//传入的参数为Promise对象,则参数的结果决定了resolve的结果
let p2 = Promise.resolve(new Promise((resolve,reject)=>{
reject('error')
}))
console.log(p2);
p2.catch(reason=>{
console.warn('出错啦');
})
结果:
2.12promise.reject方法:(reason)=>{}
reason:失败的原因
说明:返回一个失败的promise对象
返回的结果永远是失败的
let p = Promise.reject(5421);
console.log(p);
let p2 = Promise.reject('ligexiao');
console.log(p2);
let p3 = Promise.reject((resolve,reject)=>{
resolve('success');
// reject("error");
})
console.log(p3);
结果:
2.13Promise.all方法;(promises)=>{}
promises:包含n个promise的数组
说明:返回一个新的promise对象,只有数组中的所有promise成功才成功
let p1 = new Promise((resolve,reject)=>{
resolve('OK');
})
// let p2 = Promise.resolve('success');
let p2 = Promise.reject('error');
let p3 = Promise.resolve('Oh Year!');
const result = Promise.all([p1,p2,p3]);
console.log(result);
都为成功:
有一个失败:
2.14Promise.race方法:(promises)=>{}
promises:包含n个promise的数组
说明:返回一个新的promise对象,第一个完成的promise的结果状态就是最终状态
let p1 = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('OK');
})
})
// let p2 = Promise.resolve('success');
let p2 = Promise.reject('error');
let p3 = Promise.resolve('Oh Year!');
const result = Promise.race([p1,p2,p3]);
console.log(result);
结果:
2.15如何修改对象的状态
1.resolve函数
2.reject函数
3.抛出错误 => throw '出错啦';
let p = new Promise((resolve,reject)=>{
//1.resolve函数
// resolve('success');//peeding => fulfilled(resolve)
//2.reject函数
// reject('error'); //peeding => rejected
//3.抛出错误
throw '出错啦';
})
console.log(p);
2.16 一个promise指定多个成功/失败回调函数,调用问题
(当promise改变对应状态时都会调用)
let p = new Promise((reject,resolve)=>{
reject('success');
})
p.then(value=>{
console.log(value);
})
p.then(value=>{
console.log(value);
})
2.17改变状态与指定回调顺序问题
如果先指定的回调,那当状态发生改变时,回调函数就会调用,得到数据(异步)
如果先改变的状态,那当指定回调时,回调函数就会调用,得到数据(同步)
同步任务:先改变状态再执行回调
异步任务:在异步任务调reject()/resolve(),先指定回调,后改变状态
let p = new Promise((resolve,reject)=>{
// resolve('OK');//改变状态会后,回调函数调用
setTimeout(() => {//先指定回调,状态发生改变时,回调函数调用
resolve('OK');
}, 1000);
})
p.then(value=>{
console.log(value);
},reason=>{})
2.18promise.then()返回的新的promise对象由什么决定
由它的回调函数的返回值决定,如果是一个promise对象,则其根据状态返回结果,如果是非promise对象,则返回成功
let p = new Promise((resolve,reject)=>{
resolve('SUCCESS');
})
let reason = p.then(value=>{
// console.log(value);
//1.抛出错误
// throw 'error';
//2.返回的结果是非Promise对象
// return 521;
//3.返回结果是Promise对象
return new Promise((resolve,reject)=>{
// resolve('success');
reject('error');
})
},reason=>{
console.warn(reason);
})
console.log(reason);
2.19-promise如何串联多个任务
let p = new Promise((resolve,reject)=>{
setTimeout(() => {
resolve("success");
}, 1000);
})
p.then(value=>{
console.log(value);//success
}).then(value=>{console.log(value)})//undefined,这里输出的结果是上一个then的返回值,上一个then没有返回值,则为fulfilled,返回value为undefined
2.20promise异常穿透
let p = new Promise((resolve,reject)=>{
setTimeout(() => {
resolve('Success');
// reject('Error');
}, 1000);
})
p.then(value=>{
throw '出错啦';
// console.log(111);
}).then(value=>{
console.log(222);
}).then(value=>{
console.log(333);
}).catch(reason=>{
console.warn(reason);
})
2.21中断promise链
返回一个pending状态的Promise对象
return new Promise(()=>{});
let p = new Promise((resolve,reject)=>{
setTimeout(() => {
resolve('Success');
}, 1000);
})
p.then(value=>{
console.log(111);
//有且只有一个方法
return new Promise(()=>{});
}).then(value=>{
console.log(222);
}).then(value=>{
console.log(333);
}).catch(reason=>{
console.warn(reason);
})
四.Promise的自定义封装
五、async和await
1.async函数
概述:
1. async 函数的返回值为 promise 对象;
2. promise 对象的结果由 async 函数执行的返回值决定;
1.返回的结果不是一个Promise类型的对象,返回的结果就是成功的Promise对象
2.抛出错误,返回的结果是一个失败的Promise
3.返回的结果如果是一个Promise对象,结果成功就是成功,失败就是失败
async function main(){
//1.如果返回值是一个非Promise类型的数据
// return 521;
//2.如果返回的是一个Promise对象
// return new Promise((resolve,reject)=>{
// resolve('ok');
// // reject('error');
// })
//3.抛出异常
throw 'exit error';
}
let result = main();
console.log(result);
2.await表达式
概述:
1. await 必须写在 async 函数中;
2. await 右侧的表达式一般为 promise 对象;
3. await 返回的是 promise 成功的值;
4. await 的 promise 失败了 , 就会抛出异常 , 需要通过 try...catch 捕获处理;
async function main(){
let p = new Promise((resolve,reject)=>{
// resolve('Success');
reject('Error');
})
//1.右侧为Promise的情况
// let res = await p;//Success
//2.右侧为其他类型的数据
// let res = await 20;//20
//3.如果Promise是失败的状态
try{
let res3 = await p;
console.log(res3);
}
catch(e){
console.log(e);
}
}
main();
3.async与await结合读取文件
//resource 1.html 2.html 3.html 文件内容
const fs = require('fs');
const util = require('util');
const mineReadFile = util.promisify(fs.readFile);
// fs.readFile('./resource/1.html',(err,data1)=>{
// if(err) throw err;
// fs.readFile('./resource/2.html',(err,data2)=>{
// if(err) throw err;
// fs.readFile('./resource/3.html',(err,data3)=>{
// if(err) throw err;
// console.log(data1+data2+data3);
// })
// })
// })
//async 与 await
async function main(){
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(e){
console.log(e);
}
}
main();
4.async与await结合读取AJAX文件
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);
}
}
}
});
}
//段子接口地址 https://api.apiopen.top/getJoke
let btn = document.querySelector('button');
btn.addEventListener('click',async function(){
//获取段子信息
let duanzi = await sendAJAX('https://api.apiopen.top/getJoke');
console.log(duanzi);
})