目录
1.7 util.promisify 方法进行promise风格转化
1.1 Promise 是什么?
1.1.1 理解
1.抽象表达
1)promise是一门新的技术(ES6规范)
2)Promise是JS钟进行异步编程的新的解决方案(旧的:callback)
异步编程:
fs文件操作,数据库操作,AJAX,定时器
2.具体表达:
1)从语法上来说:Promise是一个构造函数
2)从功能上来说:Promise对象用来封装一个异步操作并可以获取成功/失败的结果值
1.2 为什么要用Promise?
1.2.1 指定回调函数的方式更加灵活
promise:启动异步任务=》返回对象=》给promise对象绑定回调函数(甚至可以在异步任务结束后指定/多个)
1.2.2 支持链式调用,可以用来解决回调地狱问题
1.什么是回调地狱问题?
回调函数嵌套调用,外部回调函数异步执行的结果是嵌套的回调执行的条件
2.回调地狱的缺点
不便于阅读,异常处理
3.解决方案
Promise链式调用
1.3 Promise初体验
1.3.1 promise 基本格式
<body>
<button>点击抽奖</button>
<script>
//点击按钮,1s后显示是否中奖(30%中将) 中 弹出 恭喜 不中 再接再厉
//生成随机数
function rand(m,n){
return Math.ceil(Math.random()*(n-m+1))+m-1;
}
const btn=document.querySelector("button")
btn.onclick=function(){
//普通实现
// setTimeout(() => {
// //30%怎么实现 ? 1-100 1-30中奖
// //获取1-100的随机数
// let n=rand(1,100);
// if(n<=30){
// alert('恭喜恭喜')
// }else{
// alert('再接再厉')
// }
// }, 1000);
//promise 实现 (包裹一个异步操作)
//resolve 解决 函数类型的数据 成功
//reject 拒绝 函数类型的数据 失败
const p=new Promise((resolve,reject)=>{
setTimeout(() => {
//30%怎么实现 ? 1-100 1-30中奖
//获取1-100的随机数
let n=rand(1,100);
if(n<=30){
//成功
resolve() //将promise对象的状态设置为成功
}else{
//失败
reject() //将promise对象(p)的状态设置为失败
}
}, 1000);
})
//调用then方法 接收两个参数 成功和失败的回调
p.then(()=>{
//成功的回调
alert('恭喜恭喜')
},()=>{
//失败的回调
alert('再接再厉')
})
}
</script>
</body>
</html>

1.3.2 传参
const p=new Promise((resolve,reject)=>{
setTimeout(() => {
//30%怎么实现 ? 1-100 1-30中奖
//获取1-100的随机数
let n=rand(1,100);
if(n<=30){
//成功
resolve(n) //将promise对象的状态设置为成功 可以传递结果
}else{
//失败
reject(n) //将promise对象(p)的状态设置为失败 可以传递结果
}
}, 1000);
})
//调用then方法 接收两个参数 成功和失败的回调
//value和reason是返回的成功或失败的参数
p.then((value)=>{
//成功的回调
alert('恭喜恭喜,您的中奖数字为:'+value)
},(reason)=>{
//失败的回调
alert('再接再厉,您的号码为:'+reason)
})

1.4 Promise实践练习-fs读取文件
const fs=require('fs');
//回调函数形式
// //err 错误 data 文件内容
// fs.readFile('./resource/content.txt',(err,data)=>{
// //如果出错 则抛出错误
// if(err) throw err;
// //没有错误 则输出文件内容
// console.log(data.toString());
// })
//Promise形式封装
const 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)=>{
throw reason;
})

1.5 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>
const btn=document.querySelector("button")
btn.onclick=function(){
//创建Promise
const p=new Promise((resolve,reject)=>{
const xhr=new XMLHttpRequest();
xhr.open("GET",'http://127.0.0.1:8000/server');
xhr.send();
xhr.onreadystatechange=function(){
if(xhr.readyState===4)
if(xhr.status>=200&&xhr.status<300){
resolve(xhr)
}else{
reject(xhr)
}
}
})
p.then((value)=>{
console.log(value.response);
},(reason)=>{
console.log(reason.status)
})
}
</script>
</body>
</html>
后端代码:
//引入
const express = require('express');
//创建应用对象
const app = express();
//创建路由规则
//request是对请求报文的封装
//response是对响应报文的封装
app.get('/server',(request,response)=>{
//设置响应头 设置所有都允许跨域
response.setHeader('Access-Control-Allow-Origin','*')
//设置响应体
response.send('HELLO AJAX');
});
//监听端口启动服务
app.listen(8000,()=>{
console.log('服务已启动,8000端口监听中');
})
1.6 封装一个函数mineReadFile 读取文件内容
//封装一个函数mineReadFile 读取文件内容
//参数: path 文件路径
//返回:promise 对象
function minReadFile(path){
return new Promise((resolve,reject)=>{
require('fs').readFile(path,(err,data)=>{
if(err) reject(err);
resolve(data);
})
})
}
minReadFile('./resource/content.txt')
.then(value=>{
console.log(value.toString());
},(reason)=>{
throw reason;
})

1.7 util.promisify 方法进行promise风格转化
传入一个遵循常见错误优先的回调风格的函数(以(err,value)=>...回调作为最后一个参数),并返回一个返回promise的版本(错误优先)
与1.6效果一样 实现自动封装 使用起来更方便
//引入util模块
const util=require('util')
//引入fs模块
const fs=require('fs')
//返回一个新的函数
let minReadFile=util.promisify(fs.readFile)
minReadFile('./resource/content.txt').then(value=>{
console.log(value.toString());
})

1.8 Promise封装AJAX请求
//封装一个函数sendAJAX 发送GET AJAX请求
//参数 URL
//返回结果 Promise对象
function sendAJAX(url){
return new Promise((resolve,reject)=>{
const xhr=new XMLHttpRequest();
xhr.open("GET",url);
xhr.send();
xhr.onreadystatechange=function(){
if(xhr.readyState===4)
if(xhr.status>=200&&xhr.status<300){
resolve(xhr)
}else{
reject(xhr)
}
}
})
}
sendAJAX('http://127.0.0.1:8000/server').then((value)=>{
console.log(value.response);
},(reason)=>{
console.log(reason.status)
})
2.1 promise的状态
实例对象中的一个属性【PromiseState】
有三个状态 pending resolved rejected
1.pending 变为resolved
2.pending 变为 rejected
说明:只有这两种,且不可逆
无论成功还是失败 都会有一个结果数据
成功的结果数据为 value 失败的为reason
2.2 promise对象的值
实例对象中的另一个属性 【PromiseReault】
保存着对象异步任务【成功/失败】的结果
resolve/reject
2.3 如何使用Promise?
2.3.1 API
1.Promise 构造函数:Promise(excutor){}
1)excutor函数:(resolve,reject)=>{}
2) resolve:成功
3)reject:失败
2.Promise.prototype.then 方法(onResolve,onRejected)=>{}
1)onResolve:成功的回调(value)=>{}
2)onRejected:失败的回调(reason)=>{}
3.Promise.prototype.catch方法(onRejected)=>{}
onRejected:失败的回调(reason)=>{}
4.Promise.resolve 方法:(value)=>{}
value:成功的数据或promise对象
说明:返回一个成功/失败的promise对象
如果传入的参数为 非promise对象,则返回成功的Promise对象
const p=Promise.resolve(321)
console.log(p)

如果如果传入的参数为 promise对象,则参数的结果决定了Promise.resolve的结果
const p1=Promise.resolve((resolve,reject)=>{
//resolve('OK')
reject('err')
})
console.log(p1)


5.Promise.reject方法(reason)=>{}
reason:成功的数据或promise对象
说明:返回一个失败的promise对象 (不管什么类型 都是失败)
6.Promise.all方法(promises)=>{}
promises:包含n个promise的数组
说明:返回一个新的promise,只有所有的promise都成功才成功,只要有一个失败了就直接失败
const p1=Promise.resolve((resolve,reject)=>{
resolve('OK')
})
const p2=Promise.resolve((resolve,reject)=>{
resolve('OK')
})
const p3=Promise.resolve((resolve,reject)=>{
resolve('OK')
})
const result=Promise.all([p1,p2,p3])
console.log(result)

const p1=Promise.reject((resolve,reject)=>{
resolve('ERR')
})
const p2=Promise.resolve((resolve,reject)=>{
resolve('OK')
})
const p3=Promise.resolve((resolve,reject)=>{
resolve('OK')
})
const result=Promise.all([p1,p2,p3])
console.log(result)

7.Promise.race方法(promises)=>{}
promises:包含n个promise的数组
说明:返回一个新的promise,第一个完成的promise的结果状态就是最终的结果状态
谁先运行结束 谁先输出
2.4 Promise的几个关键问题
1.如何改变状态
1)resolve()
2)reject()
3)throw
2.一个promise指定多个成功/失败的回调函数,都会调用么
当promise改变为对应状态时都会调用(都会执行)
3.改变promise状态和指定回调函数谁先谁后
都有可能,当执行器((resolve,reject)=>{ })中没有异步任务(如setTimeout。。)就先执行回调,如果有那么先改变状态
怎么先改变状态再指定回调?
在执行器中直接调用resolve/reject或者延迟更长的时间才调用then()
什么时候才能得到数据?
如果先改变状态,那么当指定回调时,回调函数就会调用,得到数据
如果先指定回调,那么当状态改变时,回调函数就会调用,得到函数
4.Promise怎么串连多个操作任务?
1)promise的then()返回一个新的promise,可以堪称then()的链式调用
2)通过then()的链式调用串连多个同步/异步任务
const p1=new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('OK')
},1000)
})
p1.then(value=>{
return new Promise((resolve,reject)=>{
resolve('success')
})
}).then(value=>{
console.log(value);//success
}).then(value=>{
console.log(value);//undefind因为 没有return value的返回值
})
5.promise的异常穿透
当使用promise的then链式调用时 ,可以在最后指定失败的回调
前面的任何操作出了异常,都会传到最后的回调中处理(只处理检测到的第一个异常,如果多个异常值返回一个结果)
const p1=new Promise((resolve,reject)=>{
setTimeout(()=>{
reject('err')
},1000)
})
p1.then(value=>{
console.log(1);
}).then(value=>{
console.log(2);
}).then(value=>{
console.log(3);
}).catch(reason=>{
console.warn(reason)
})
![]()
6.中断promise链
const p1=new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('err')
},1000)
})
p1.then(value=>{
console.log(1);
//中断promise的方法
//改变状态为pending
return new Promise(()=>{})
}).then(value=>{
console.log(2);
}).then(value=>{
console.log(3);
}).catch(reason=>{
console.warn(reason)
})
![]()
3.1 手写Promise
class Promise{
//构造方法
constructor(exexutor){
//特性
//改变状态 三种方法resolve() reject() throw
//状态只能改变一次
//添加属性
this.PromiseState='pending';
this.PromiseResult=null;
this.callbacks=[];
//改变this指向(这里的this指向window,要让他指向此函数)
const self=this;
//resolve函数
function resolve(data){
//判断promiseState状态有没有改变 如果改变了那么就不执行了
if(self.PromiseState!=='pending') return
//改变状态(promiseState)属性修改
self.PromiseState='resolved'
//设置结果值(promiseResult)
self.PromiseResult=data;
//调用成功的回调函数 遍历数组
setTimeout(() => {
self.callbacks.forEach(item=>{
item.onResolved(data)
})
});
}
//reject函数
function reject(data){
if(self.PromiseState!=='pending') return
//改变状态(promiseState)属性修改
self.PromiseState='rejected'
//设置结果值(promiseResult)
self.PromiseResult=data;
setTimeout(() => {
self.callbacks.forEach(item=>{
item.onRejected(data)
})
});
}
try {
//执行器函数同步调用
exexutor(resolve,reject)
} catch (e) {
//throw抛出异常后 也要改变状态
reject(e)
}
}
//添加then方法
//特性
//三种状态都有对应函数
//一个promise指定多个then的回调函数,都会调用
//同步任务 then的返回结果 由promise的返回结果决定的 promise是成功那么 then的结果就是成功 会议一个throw的结果是失败
then(onResolved,onRejected){
const self=this
//异常穿透
//判断回调函数参数
if(typeof onRejected!=='function'){
onRejected=reason=>{
throw reason
}
}
if(typeof onResolved!=='function'){
onResolved=value=>value
}
return new Promise((resolve,reject)=>{
//封装函数
function callback(type){
try{
//先执行成功的回调函数 拿到结果
const result = type(self.PromiseResult)
//判断结果是成功的还是失败的 改变状态
if(result instanceof Promise){
result.then(v=>{
//成功调用 resolve
resolve(v)
},r=>{
//失败调用 reject
reject(r)
})
}else{
//结果的状态为成功
resolve(result);
}}catch(e){
reject(e)
}
}
if(this.PromiseState==='pending') {
//保存回调函数 把每次调用then的value 和 reason 函数都存在数组中
this.callbacks.push({
onResolved:function(){
callback(onResolved)
},
onRejected:function(){
callback(onRejected)
}
})
}
if(this.PromiseState==='resolved') {
setTimeout(() => {
callback(onResolved)
});
}
if(this.PromiseState==='rejected') {
setTimeout(() => {
callback(onRejected)
});
}
})
}
//添加catch方法
//特性
//失败的回调
catch(onRejected){
return this.then(undefined,onRejected)
}
//添加resolve方法
//特性
//返回一个成功/失败的promise对象
//如果传入的参数为 非promise对象,则返回成功的Promise对象
//如果如果传入的参数为 promise对象,则参数的结果决定了Promise.resolve的结果
//这个方法不属于实例对象 而属于这个类 所以要加static
static resolve(value){
return new Promise((resolve,reject)=>{
if(value instanceof Promise){
value.then(v=>{
resolve(v)
},r=>{
reject(r)
})
}else{
//状态设置为成功
resolve(value)
}
})
}
//添加reject方法
//返回的永远是失败的promise对象
static reject(reason){
return new Promise((resolve,reject)=>{
reject(reason)
})
}
//添加all方法
//返回一个新的promise,只有所有的promise都成功才成功,只要有一个失败了就直接失败
static all(promises){
return new Promise((resolve,reject)=>{
//遍历数组
let count = 0
let arr=[]
for(let i=0;i<promises.length;i++){
promises[i].then(v=>{
//得知对象的状态是成功的 每个对象都成功就可以调用resolve函数了
count++;
//将当前promise对象成功的结果存放在数组中
arr[i]=v;
if(count===promises.length){
resolve(arr)
}
},r=>{
reject(r)
})
}
})
}
//添加race方法
//返回一个新的promise,第一个完成的promise的结果状态就是最终的结果状态 谁先运行结束 谁先输出
static race(promises){
return new Promise((resolve,reject)=>{
//遍历数组
let count = 0
let arr=[]
for(let i=0;i<promises.length;i++){
promises[i].then(v=>{
//得知对象的状态是成功的 [成功]
resolve(v)
},r=>{
reject(r)
})
}
})
}
}
4.1 async和await
4.1.1 async 函数
1.函数的返回值是promise对象
2.promise对象的结果由async函数执行的返回值决定
如果传入的参数为 非promise对象,则返回成功的Promise对象
如果传入的参数为promise对象,则返回promise对象的状态(成功/失败)
如果抛出异常,返回结果是失败的对象
4.1.2 await表达式
1.await右侧的表达式一般为promise对象,但耶可以是其他值
2.如果表达式是promise对象,await返回的结果是promise成功的值
3.如果是其他值,直接将此值作为await的返回值
4.1.3 注意
1.await必须写在async函数中,但是async函数中可以没有await
2.如果await的promise失败了,就会抛出异常,需要通过try...catch捕获处理
async function main(){
let p=new Promise((resolve,reject)=>{
//resolve('OK');
reject('err')
})
//1.右侧为promise的情况
let res=await p;
console.log(res) //ok
//2.右侧为非promise的情况
let res2=await 20;
console.log(res2)//20
//如果promise是失败的状态
try{
let res3=await p;
}catch(e){
console.log(e)
}
}
main();
![]()
![]()
4.1.4 实践1
拿到三个文件内容
//读取1,2,3.html内容
//回调函数的方式
const fs=requier('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 function main(){
try {
let d1=await mineReadFile('./resource/1.html')
let d2=await mineReadFile('./resource/2.html')
let d3=await mineReadFile('./resource/2.html')
console.log(d1+d2+d3)
} catch (error) {
console.log(error)
}
}
main();
4.1.5实践2 发送ajax请求
<body>
<!-- 点击按钮 获取信息 -->
<button id="btn">点击获取段子</button>
<script>
function sendAJAX(url){
return new Promise((resolve,reject)=>{
const xhr=new XMLHttpRequest();
xhr.open("GET",url);
xhr.send();
xhr.onreadystatechange=function(){
if(xhr.readyState===4)
if(xhr.status>=200&&xhr.status<300){
resolve(xhr)
}else{
reject(xhr)
}
}
})
}
//段子获取接口 https://api.apiopen.top/getJoke
//获取元素
const btn=document.querySelector('#btn')
btn.addEventListener('click',async function(){
//获取段子信息
let joke=await sendAJAX('https://api.apiopen.top/getJoke')
console.log(joke)
})
</script>
</body>
本文详细介绍了Promise的概念,包括其解决回调地狱问题的原因、基本格式及实践应用,如读取文件、封装AJAX请求。此外,还深入探讨了Promise的状态、API、关键问题以及如何手写Promise。同时,文章涵盖了async和await的使用,包括它们的功能、注意点及实际操作示例。
534

被折叠的 条评论
为什么被折叠?



