Promise入门到精通

本文详细介绍了Promise的概念,包括其解决回调地狱问题的原因、基本格式及实践应用,如读取文件、封装AJAX请求。此外,还深入探讨了Promise的状态、API、关键问题以及如何手写Promise。同时,文章涵盖了async和await的使用,包括它们的功能、注意点及实际操作示例。

目录

1.1 Promise 是什么?

1.1.1 理解

1.2 为什么要用Promise?

1.2.1 指定回调函数的方式更加灵活

1.2.2 支持链式调用,可以用来解决回调地狱问题

1.3 Promise初体验

1.3.1 promise 基本格式

1.3.2 传参

 1.4 Promise实践练习-fs读取文件

 1.5 AJAX请求操作封装

1.6 封装一个函数mineReadFile 读取文件内容

 1.7 util.promisify 方法进行promise风格转化

 1.8 Promise封装AJAX请求

2.1 promise的状态

2.2 promise对象的值

2.3 如何使用Promise?

2.3.1 API

2.4 Promise的几个关键问题

3.1 手写Promise

4.1 async和await

4.1.1 async 函数

4.1.2 await表达式

4.1.3 注意

 4.1.4 实践1

4.1.5实践2 发送ajax请求


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>

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值