当前位置:网站首页>什么是Promise?Promise的原理是什么?Promise怎么用?
什么是Promise?Promise的原理是什么?Promise怎么用?
2022-07-31 00:18:00 【薇z】
无论天空如何阴霾,太阳一直都在,不在这里,就在那里
接下来让我们一起学习promise
吧~
Promise它是es6异步编程新的解决方案,从语法来说呢,它就是一个构造函数,可以封装异步的任务,并且可以对结果进行处理。Promise的最大好处就是可以解决回调地狱的问题,并且它在指定回调以及错误处理这一块会更加灵活和方便,并且Promise在现在的项目当中,无论是web还是app当中,都应用的十分的广泛无论是前端还是后端,都可以看见Promise的身影,而且它也是现在面试的高频题目,如果想要进入大厂的话,一定要学会Promise的内部原理和使用,下面文章是我学习promise的总结,一起加油丫~
文章目录:
1.Promise的介绍
1.1Promise是什么呢?
1.抽象表达
①Promise是一门新的技术(es6规范)
②Promise是javascript中进行异步编程
的新解决方案
异步编程: 1.fs 文件操作 require('fs').readFile('./index.hetml',(err,data)=>{
}) 2.数据库操作 3.AJAX $.get('./server',(data)=>{
}) 4.定时器 setTimeOut(()=>{
})
注:旧方案是单纯使用回调函数,容易造成回调地狱
2.具体表达
①从语法上来讲:Promise是一个构造函数
②从功能上来讲:Promise对象用来封装一个异步操作并可以获取成功/失败的结果值
1.2为什么要用Promise?
1.指定回调函数的方式更加灵活
①旧的方法:必须在启动任务前指定
②用Promise:启动异步任务–返回Promise对象–给Promise对象绑定回调函数(甚至可以在异步任务结束后指 定/多个)
2.支持链式调用,可以解决回调地狱的问题
①什么是回调地狱?
回调函数嵌套调用,外部回调函数异步执行的结果的嵌套的回调执行的条件
我们来看一下回调地狱典型的一个例子
function1(opt1,(..args1)=>{
function1(opt2,(..args2)=>{
function1(opt3,(..args3)=>{
function1(opt4,(..args4)=>{
//一个函数里边又套着另外一个异步任务
//层级在不断推进
})
})
})
})
②回调地狱的缺点
不便于阅读,不便于异常的处理
③解决方案
Promise的链式调用
2.Promise的基本使用
1.promise初体验
接下来写一个小案例
案例要求: 点击按钮, 1s 后显示是否中奖(30%概率中奖), 若中奖弹出 “恭喜恭喜, 奖品为 10万 RMB 劳斯莱斯优惠券,您的中奖数字为。。。”,若未中奖弹出 “再接再厉,您的号码为。。。”
(1).用普通方法实现和promise形式实现
<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>
结果演示图片:
2.promise的实践练习
(1)promise实践练习–fs模块
案例:读取content.txt的内容,成功则把内容输出。如果出错则抛出错误
//
const fs = require('fs');
//回调函数 形式
// fs.readFile('./resource/content.txt', (err, data) => {
// // 如果出错 则抛出错误
// if(err) throw err;
// //输出文件内容
// console.log(data.toString());
// });
//Promise 形式
let p = new Promise((resolve , reject) => {
fs.readFile('./resource/content.txt', (err, data) => {
//如果出错
if(err) reject(err);
//如果成功
resolve(data);
});
});
//调用 then
p.then(value=>{
console.log(value.toString());
}, reason=>{
console.log(reason);
});
结果演示图片:
(2)promise实践练习–封装fs
一个js文件:
封装一个函数 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);
});
封装完之后仍然可以实现相应功能,且使用更加灵活
(3)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://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>
(4)promise实践练习–封装ajax请求
封装一个函数 sendAJAX 发送 GET AJAX 请求
参数 URL
返回结果 Promise 对象
<body>
<script>
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('http://wthrcdn.etouch.cn/weather_mini')
.then(value => {
console.log(value);
}, reason => {
console.warn(reason);
});
</script>
</body>
3.util.promisify方法的使用
util.promisify(original);oringinal :<function>
;返回:<function>
传入一个遵循常见的错误优先的回调风格的函数(即以(err,value)=>{...}回调作为最后一个参数),并返回一个返回promise的版本
//引入 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,不需要每一个方法都要手动的去进行封装,而是可以借助util.promisify这一个方法将原来的回调函数封合的方法转变成promise风格的函数,这样使用会方便一些。
通过上面几个案例了解了promise的应用,接下来进入到promise的理论层面
3.Promise的原理
1.promise的状态改变
promise的状态是指 实例对象中的一个属性,这个属性称为 PromiseState
该状态有三种可能的值:
- pedding 初始化的值,未决定的
- resolved/fullfilled 成功
- rejected 失败
状态改变的两种可能:
- pedding变为resolved
- pedding变为rejected
说明:只有这两种状态的转变,且一个promise对象只能改变一次,无论变为成功还是失败,都会有一个结果的数据,成功的结果数据一般为value,失败的结果数据一般称为reason
2.Promise对象的值
指:实例对象中的另一个属性:PromiseResult
,保存着异步任务的【成功/失败】的结果,resolve()和reject()
3.promise的工作流程
首先需要通过new Promise创建一个对象。在promise内部封装异步操作,如果异步操作成功,则调用resolve()函数,resolve一调,将Promise的状态改为成功即resolved状态,成功后调用then方法时,调用第一个回调函数中的代码,然后返回一个新的promise对象。如果异步操作失败,则调用reject函数,reject一调,将Promise的状态改为失败即rejected状态,失败后调用then方法时,调用第二个回调函数中的代码,然后返回一个新的promise对象。
通过上了解了promise的原理之后,接下来进入到如何使用promise
4.如何使用Promise?
1.promise的API
(1)Promise构造函数:Promise(excutor){}
①executor函数:执行器 (resolve,reject)=>{}
②resolve函数:内部定义成功时我们调用的函数 value=>{}
③reject函数:内部定义失败时我们调用的函数 reson=>{}
说明:executor会在内部立即同步调用
,异步操作在执行器中执行
let p=new Promise((resolve,reject)=>{
//同步调用
console.log(111)
})
console.log(222)
输出:
111
222
(2)Promise.prototype.then方法:(onResolved,onRejected)=>{}
①onResolved函数:成功的回调函数 (value)=>{}
②onRejected函数:成功的回调函数 (reson)=>{}
说明:指定用于得到成功的回调和用于得到失败reason的失败的回调,返回一个新的promise对象
(3)Promise.prototype.catch方法:(onRejected)=>{}
①onRejected函数:失败的回调函数 (reson)=>{}
let p=new Promise((resolve,reject)=>{
reject('error')
})
p.catch(reason=>{
console.log(reason)//error
})
说明:then的语法糖,相当于:then(undefined,onRejected)
(4)Promise.resolve方法:(value)=>{}
①value:成功的数据或promise对象
说明:返回一个成功或者失败的promise对象
//如果传入的参数为 非Promise类型的对象,则返回的结果为成功promise对象
let p1=Promise.resolve(520)
console.log(p1)//520
//如果传入的参数为Promise对象,则参数的结果决定了resolve的结果
let p2=Promise.resolve(new Promise((resolve,reject)=>{
// resolve("ok")
reject("Error")
}))
//console.log(p1) //ok
p2.catch(reason=>{
console.log(reason)
})
(5)Promise.reject 方法: (reason) => {}
① reason: 失败的原因
说明: 返回一个失败的 promise 对象
<script>
let p = Promise.reject(521);
let p2 = Promise.reject('iloveyou');
let p3 = Promise.reject(new Promise((resolve, reject) => {
resolve('OK');
}));
console.log(p);
console.log(p2);
console.log(p3);
</script>
失败图片:
(6)Promise.all 方法: (promises) => {}
① promises: 包含 n 个 promise 对象的数组
说明: 返回一个新的 promise, 只有所有的 promise 都成功才成功, 只要有一个失败了就
直接失败
<script>
let p1 = new Promise((resolve, reject) => {
resolve('OK');
})
let p2 = Promise.resolve('Success');
//let p2 = Promise.reject('Error');
let p3 = Promise.resolve('Oh Yeah');
const result = Promise.all([p1, p2, p3]);
console.log(result);
</script>
成功结果:
失败结果:
(7)Promise.race 方法: (promises) => {}
(1) promises: 包含 n 个 promise 的数组
说明: 返回一个新的 promise, 第一个完成的 promise 的结果状态就是最终的结果状态
<script>
let p1 = new Promise((resolve, reject) => {
resolve('OK');
})
let p2 = Promise.resolve('Success');
let p3 = Promise.resolve('Oh Yeah');
//调用
const result = Promise.race([p1, p2, p3]);
console.log(result);
</script>
结果:
2.Promise的几个关键问题
1.如何改变promise状态?
①resolve(value):如果当前是pending就会变为resolved(fulfilled)
②reject(reason):如果当前是pending就会变为rejected
③抛出异常throw:如果当前是pending就会变为rejected
2.一个promise指定多个成功/失败回调函数,都会调用吗?
当promise改变为对应状态时都会调用
3.改变promise状态和then回调谁先谁后?
(1)都有可能,正常情况下是先指定then回调再改变状态,但也可以先改状态再指定then回调
(2)如何先改状态再指定then回调
①在执行器中直接调用resolve()/reject()
②延迟更长时间才调用then()
(3)什么时候才能得到数据?
①如果先指定的then回调,那当状态发生改变时,回调函数就会调用,得到数据
②如果先改变的状态,那当指定then回调时,回调函数就会调用,得到数据
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的链式调用串连多个同步/异步任务
<script>
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('OK');
}, 1000);
});
p.then(value => {
return new Promise((resolve, reject) => {
resolve("success");
});
}).then(value => {
console.log(value);//success
}).then(value => {
console.log(value);//undefined
})
</script>
6.promise的异常传透
(1)当使用promise的then链式调用时,可以在最后指定失败的回调
(2)前面任何操作出了异常,都会传到最后失败的回调中处理
<script>
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('OK');
// reject('Err');
}, 1000);
});
p.then(value => {
// console.log(111);
throw '失败啦!';
}).then(value => {
console.log(222);
}).then(value => {
console.log(333);
}).catch(reason => {
console.warn(reason);
});
</script>
7.中断 promise 链?
(1) 当使用 promise 的 then 链式调用时, 在中间中断, 不再调用后面的回调函数
(2) 办法: 在回调函数中返回一个 pendding 状态的 promise 对象
说明:pedding的状态没有改变,就不会去执行下边的回调
<script>
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('OK');
}, 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);
});
</script>
5.async 与 await
1.async 函数
①函数的返回值为 promise 对象
②promise 对象的结果由 async 函数执行的返回值决定
<script>
//then
async function main(){
//1. 如果返回值是一个非Promise类型的数据
// return 521;
//2. 如果返回的是一个Promise对象
// return new Promise((resolve, reject) => {
// // resolve('OK');
// reject('Error');
// });
//3. 抛出异常
throw "Oh NO";
}
let result = main();
console.log(result);
</script>
2.await 表达式
①await 右侧的表达式一般为 promise 对象, 但也可以是其它的值
②如果表达式是 promise 对象, await 返回的是 promise 成功的值
③如果表达式是其它值, 直接将此值作为 await 的返回值
说明:.await 必须写在 async 函数中, 但 async 函数中可以没有 await
如果 await 的 promise 失败了, 就会抛出异常, 需要通过 try…catch 捕获处理
<script>
async function main(){
let p = new Promise((resolve, reject) => {
// resolve('OK');
reject('Error');
})
//1. 右侧为promise的情况
// let res = await p;
//2. 右侧为其他类型的数据
// let res2 = await 20;
//3. 如果promise是失败的状态
try{
let res3 = await p;
}catch(e){
console.log(e);
}
}
main();
</script>
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.code);
}
}
main();
结果:
<body>
<button id="btn">点击获取天气</button>
<script>
//axios
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);
}
}
}
});
}
let btn = document.querySelector('#btn');
btn.addEventListener('click',async function(){
//获取信息
let duanzi = await sendAJAX('http://wthrcdn.etouch.cn/weather_mini');
console.log(duanzi);
});
</script>
</body>
结果:
对于promise的学习暂时到这里就结束啦
边栏推荐
- joiplay模拟器不支持此游戏类型怎么解决
- .NET Cross-Platform Application Development Hands-on Tutorial | Build a Kanban-style Todo App with Uno Platform
- Steven Giesel recently published a 5-part series documenting his first experience building an application with the Uno Platform.
- VSCode高效开源神器有哪些
- MySQL数据库进阶篇
- Point Cloud Scene Reconstruction with Depth Estimation
- xss的绕过
- 46.<list链表的举列>
- How to import game archives in joiplay emulator
- transition过渡&&animation动画
猜你喜欢
随机推荐
Common network status codes
The difference between substring and substr in MySQL
How to install joiplay emulator rtp
从编译的角度来学作用域!
background对float的子元素无效
数据库的严格模式
A Brief Talk About MPI
[In-depth and easy-to-follow FPGA learning 15---------- Timing analysis basics]
[Deep learning] Detailed explanation of Transformer model
transition transition && animation animation
web漏洞之需要准备的工作
Asser uses ant sword to log in
Bypass of xss
宽客必备神器-AKShare
The difference between h264 and h265 decoding
从笔试包装类型的11个常见判断是否相等的例子理解:包装类型、自动装箱与拆箱的原理、装箱拆箱的发生时机、包装类型的常量池技术
MySQL数据库(基础)
Shell脚本 if语句
从两个易错的笔试题深入理解自增运算符
借助深度估计的点云场景重建