搞不懂promise?看这篇就够了

文章详细介绍了Promise的基本用法,如解决回调地狱问题、状态管理以及Promise的静态方法如.all、.race等。同时,文章阐述了async函数的使用,如何通过await关键字将异步代码写得更像同步,以及异常处理机制。
摘要由CSDN通过智能技术生成

1、什么是promise

promise 是一个 ES6 的语法 承诺的意思,是一个专门用来解决异步 回调地狱 的问题

2、什么是回调地狱

  • 当一个回调函数嵌套一个回调函数的时候
  • 就会出现一个嵌套结构
  • 当嵌套的多了就会出现回调地狱的情况
  • 比如我们发送三个 ajax 请求
  1. 第一个正常发送
  2. 第二个请求需要第一个请求的结果中的某一个值作为参数
  3. 第三个请求需要第二个请求的结果中的某一个值作为参数
		ajax({
            url: "我是第一个请求",
            success: function (res) {
                console.log(res);//打印第一个请求结果
                // 发送第二个请求
                ajax({
                    url: "我是第二个请求",
                    data: {
                        name: "张三"
                    },
                    success: function (res1) {
                        console.log(res1);//打印第二个请求结果
                        //发送第三个请求
                        ajax({
                            url: "我是第三个请求",
                            method: "POST",
                            data: {
                                name: "李四",
                                age: 18
                            },
                            success: function (res2) {
                                console.log(res2);//打印第三个请求结果
                            }
                        })
                    }
                })
            }
        })
  • 回调地狱,其实就是回调函数嵌套过多导致的。
  • 当代码成为这个结构以后,已经没有维护的可能了
  • 所以我们要把代码写的更加的艺术一些。这个时候就需要用到我们的promise对象

3、promise的基本使用

在利用promise对象来解决回调函数之前我们先来了解一下promise的基本使用。

3.1 promise基本语法

  • promise 就是一个语法
  • 当我们new一个promise,此时我们需要传递一个回调函数,这个函数为立即执行的,称之为(executor
  • 这个回调函数,我们需要传入两个参数回调函数,reslove,reject(函数可以进行传参)
  1. 当执行了reslove函数,会回调promise对象的.then函数
  2. 当执行了reject函数,会回调promise对象的.catche函数

举个栗子:

  	function resultData(data) {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    if (data === 'list') {
                       resolve("成功了,数据上传成功")
                    } else {
                       reject("失败了,请检查数据是否正确")
                    }
                }, 1000)
            })
        }
        //请求成功
        resultData('list').then(resolve => {
            console.log(resolve);
        })//成功了,数据上传成功
        //请求失败
        resultData('lit').catch(reject => {
            console.log(reject);
        })//失败了,请检查数据是否正确

3.2 promise的状态

  1. 使用promise的时候,给它一个承诺,我们可以将他划分为三个阶段
  • pending(待定),执行了executor,状态还在等待中,没有被兑现,也没有被拒绝
  • fulfilled(已兑现),执行了resolve函数则代表了已兑现状态
  • rejected(已拒绝),执行了reject函数则代表了已拒绝状态
  1. 简单理解:
  • 你承诺自己明天开始好好学习(还未到明天,此时为待定状态
  • 时光飞逝,到了第二天你去图书馆好好学习了(已兑现状态
  • 时光飞逝,到了第二天你因为某些事情而无法去图书馆学习(已拒绝状态
  1. 状态只要从待定状态,变为其他状态后,则状态不能再改变。
  • 比如从pengding状态变为fulfilled状态后,则不能在从fulfilled状态变为rejected状态
  • 同样从pengding状态变为rejected状态后,则不能在从rejected状态变为fulfilled状态

举个栗子:

	const p = new Promise((resolve,reject)=>{
            setTimeout(()=>{
                resolve('成功了')
                reject('失败了')
            },1000)
        })
        p.then(resolve=>console.log(resolve)).catch(reject=>console.log(reject))
        //成功了

当我调用resolve之后,在调用reject是无效的,因为状态已经发生改变,并且是不可逆的。

3.3 resolve不同值的区别

resolve() 和reject()只能接受并处理一个参数,多余的参数会被忽略掉。
如果想传递多个数据,需要用数组,或者对象的方式来进行传参。

	function resultData(data) {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    if (data === 'list') {
                       resolve(["成功了,数据上传成功","你可以进行下一步操作了"])
                    } else {
                       reject({one:"失败了,请检查数据是否正确",two:"需要重新上传"})
                    }
                }, 1000)
            })
        }
         //请求成功
        resultData('list').then(resolve => {
            console.log(resolve[0],resolve[1]);//成功了,数据上传成功 你可以进行下一步操作了
        })
         //请求失败
        resultData('lit').catch(reject => {
            console.log(reject.one,reject.two);//失败了,请检查数据是否正确 需要重新上传
        })

在正确的时候调用resolve函数,失败的时候调用reject函数,把需要的参数传递出去。

如果resolve中传入的是另外一个Promise,那么这个新Promise会决定原Promise的状态

   const result = new Promise((resolve, reject) => {
            resolve(new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve('你好')
                }, 1000);
            }))
        })

        result.then(res => console.log(res))

3.4 Promise中的类方法/静态方法

3.4.1 Promise.reslove

		Promise.resolve('hello').then(res => console.log(res))//hello
        // 等价于
        const p = new Promise((resolve, reject) => resolve('hello'))
        p.then(res => console.log(res))//hello

有的时候,你预知状态的结果为fulfilled,则可以用这种简写方式

3.4.2 Promise.reject

		Promise.reject('hello').catch(err => console.log(err))/hello
        // 等价于
        const p = new Promise((resolve, reject) => reject('hello'))//hello
        p.catch(err => console.log(err))

有的时候,你预知状态的结果为rejected,则可以用这种简写方式

3.4.3 Promise.all

fulfilled状态

	const promiseOne = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('hello 张三')
            }, 1000);
        })

        const promiseTwo = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('hello 李四')
            }, 2000);
        })

        const promiseThree = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('hello 王五')
            }, 3000);
        })
        Promise.all([promiseOne, promiseTwo, promiseThree]).then(res => console.log(res))
        //['hello 张三', 'hello 李四', 'hello 王五']
  • 只有三个都为resolve状态的时候才会调用.then方法。
  • 只要有一个promise的状态为rejected,则会回调.catch方法。

rejected状态

	const promiseOne = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('hello 张三')
            }, 1000);
        })

        const promiseTwo = new Promise((resolve, reject) => {
            setTimeout(() => {
                reject('hello 李四')
            }, 2000);
        })

        const promiseThree = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('hello 王五')
            }, 3000);
        })
        Promise.all([promiseOne, promiseTwo, promiseThree]).then(res => console.log(res)).catch(err=>console.log(err))
        
  • 当遇到rejectd的时候,后续的promise结果我们是获取不到,并且会把reject的实参,传递给catch的err形参中去,所以最后的结果是hello 李四

3.4.4 Promise.allSettled

上面的Promise.all有一个缺陷,就是当遇到一个rejected的状态,那么对于后面是resolve或者reject的结果我们都无法拿到。
Promise.allSettled,无论状态是fulfilled/rejected都会把参数返回给我们

 	const promiseOne = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('hello 张三')
            }, 1000);
        })

        const promiseTwo = new Promise((resolve, reject) => {
            setTimeout(() => {
                reject('hello 李四')
            }, 2000);
        })

        const promiseThree = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('hello 王五')
            }, 3000);
        })
        Promise.allSettled([promiseOne, promiseTwo, promiseThree]).then(res => console.log(res))

在这里插入图片描述

注意:该方法如果其中一个promise没有结果,则会什么都不打印。
简单理解:如果没有调用resolve()或者reject(),该promise就无法得到一个结果,其中一个没有结果,则什么都不会打印。

 	const promiseOne = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('hello 张三')
            }, 1000);
        })

        const promiseTwo = new Promise((resolve, reject) => {
            setTimeout(() => {
                reject('hello 李四')
            }, 2000);
        })

        const promiseThree = new Promise((resolve, reject) => {
            setTimeout(() => {
                
            }, 3000);
        })
        Promise.allSettled([promiseOne, promiseTwo, promiseThree]).then(res => console.log(res))
      //其中一个promise没有结果,则什么都不打印

3.4.5 Promise.race

  • 优先获取第一个返回的结果,无论结果是fulfilled还是rejectd
	 const promiseOne = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('hello 张三')
            }, 2000);
        })

        const promiseTwo = new Promise((resolve, reject) => {
            setTimeout(() => {
                reject('hello 李四')
            }, 1000);
        })
        Promise.race([promiseOne, promiseTwo]).then(res => console.log(res)).catch(err=>console.log(err))

结果是hello 李四

3.4.6 Promise.any

  • 只获取第一个状态为fulfilled,如果全部为rejected则报错
	const promiseOne = new Promise((resolve, reject) => {
            setTimeout(() => {
                reject('hello 张三')
            }, 2000);
        })

        const promiseTwo = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('hello 李四')
            }, 3000);
        })
        Promise.any([promiseOne, promiseTwo]).then(res => console.log(res)).catch(err=>console.log(err))

打印的结果是hello 李四

 const promiseOne = new Promise((resolve, reject) => {
            setTimeout(() => {
                reject('hello 张三')
            }, 2000);
        })

        const promiseTwo = new Promise((resolve, reject) => {
            setTimeout(() => {
                reject('hello 李四')
            }, 3000);
        })
        Promise.any([promiseOne, promiseTwo]).then(res => console.log(res)).catch(err=>console.log(err))

直接报错:提醒你AggregateError:所有承诺都被拒绝
在这里插入图片描述

4、使用promise解决回调地狱的问题

4.1 promise封装Ajax


//如果data传入的是json格式,而header设置的是form表单编码
//我们就需要将json字符串进行转换。
function queryStringify(obj) {
    let str = ''
    for (let k in obj) str += `${k}=${obj[k]}&`
    //username=kerwin&password=789&
    return str.slice(0, -1)
}

// 封装 ajax
function ajax(options) {
    //设置一个options默认值,在我们不传递参数的时候默认是get提交
    let defaultoptions = {
        url: "",
        method: "GET",
        async: true,
        data: {},
        headers: {
            "content-type": "application/x-www-form-urlencoded"
        },
        success: function () { },
        error: function () { }
    }
    // 进行解构赋值
    let { url, method, async, data, headers, success, error } = {
    	//展开运算符
        ...defaultoptions,
        ...options
    }
    //判断headers的值是否为json
    if (typeof data === 'object' && headers["content-type"]?.indexOf("json") > -1) {
        data = JSON.stringify(data)
    }
    else {
        data = queryStringify(data)
    }
    // // 如果是 get 请求, 并且有参数, 那么直接组装一下 url 信息
    if (/^get$/i.test(method) && data) url += '?' + data

    // // 4. 发送请求
    const xhr = new XMLHttpRequest()
    xhr.open(method, url, async)
    xhr.onload = function () {
        if (!/^2\d{2}$/.test(xhr.status)) {
            // console.log(error)
            error(`错误状态码:${xhr.status}`) //回调
            return
        }

        // 执行解析
        try {
            let result = JSON.parse(xhr.responseText)
            success(result)
        } catch (err) {
            error('解析失败 ! 因为后端返回的结果不是 json 格式字符串')
        }
    }


    // // 设置请求头内的信息
    for (let k in headers) xhr.setRequestHeader(k, headers[k])
    //判断method是不是get
    if (/^get$/i.test(method)) {
        xhr.send()
    } else {
        xhr.send(data)
    }
}

对此版本封装成promise版本

function pajax(options){
	//返回一个promise对象
    return  new Promise((resolve,reject) => {
        ajax({
            ...options,
            success(res){
                resolve(res)
            },
            error(err){
                reject(err)
            }
        })
    }

4.2 promise链式调用解决回调地狱

 	pajax({
            url: "我是第一个请求"
        }).then(res => {
            console.log(res);//打印第一个请求结果
            return pajax({
                url: "我是第二个请求",
                data: {
                    name: "张三"
                }
            })
        }).then(res => {
            console.log(res);//打印第二个请求结果
           	return pajax({
                url: "我是第三个请求",
                method: "POST",
                data: {
                    name: "李四",
                    age: 18
                }
            })
        }).then(res => {
            console.log(res);//打印第三个请求结果
        }).catch(err=>{
            console.log('错误',err);
        })

形成一种链式调用,解决了回调地狱的问题。
但是不够优雅,思考一下能不能写成同步的方式呢?

5、async/await

5.1 回调地狱的终极解决方案

异步代码最高的境界就是看起来像同步代码

  	async function test() {
            const res1 = await pajax({
                url: "我是第一个请求"
            })
            const res2 = await pajax({
                url: "我是第二个请求",
                data: {
                    name: "张三"
                }
            })
            const res3 = await pajax({
                url: "我是第三个请求",
                method: "POST",
                data: {
                    name: "李四",
                    age: 18
                }
            })
        }

5.2 async

语法:

	async function test() {
	const res = await promise对象
	}
  1. 这个是一个特殊的函数方式
  2. 可以 await 一个 promise 对象
  3. 可以把异步代码写的看起来像同步代码
  4. 只要是一个 promiser 对象,那么我们就可以使用 async/await 来书写

5.2.1 async内部代码同步执行

异步函数的内部代码执行过程和·普通的函数是一致的,默认情况下也是会被同步执行,只在内部产生同步执行,并不会影响函数外面的执行过程。

 	async function sayHi() {
            console.log('我是异步函数1')
            console.log('我是异步函数2')
        }
        console.log('我是外层');
        sayHi()

在这里插入图片描述

5.2.2 异步函数的返回值

  • 普通函数主动返回什么就返回什么,不返回为undefined
  • 异步函数的返回值特点

明确返回一个promise,则由这个promise决定

5.2.3异步函数的异常处理

  • 如果函数内部中途发生错误,可以通过函数的返回值.catch进行捕获

还是拿前面的Ajax案例来举例:

async function test() {
            const res1 = await pajax({
                url: "我是第一个请求"
            })
            const res2 = await pajax({
                url: "我是第二个请求",
                data: {
                    name: "张三"
                }
            })
            const res3 = await pajax({
                url: "我是第三个请求",
                method: "POST",
                data: {
                    name: "李四",
                    age: 18
                }
            })
        }
        test().then(res => console.log('成功', res)).catch(err => console.log('失败', err))
  • 如果函数内部中途发生错误,也可以通过try catch的方式捕获异常
 async function test() {
            try {
                const res1 = await pajax({
                    url: "我是第一个请求"
                })
                const res2 = await pajax({
                    url: "我是第二个请求",
                    data: {
                        name: "张三"
                    }
                })
                const res3 = await pajax({
                    url: "我是第三个请求",
                    method: "POST",
                    data: {
                        name: "李四",
                        age: 18
                    }
                })
            } catch (err) {
                console.log('失败', err)
            }
        }

5.3 await解析

1.await关键字必须写在async声明的函数里面
2. 这个promise状态变为fulfilled才会执行await后续的代码,所以await后面的代码,相当于包括在.then方法的回调中。

  1. 返回值:
  • 如果右侧为promise对象,则返回promise的fulfilled结果
  • 如果右侧为其他类型的数据则返回该类型的值
  • 如果promise是失败的状态,则需要在函数内部try catch,或者进行链式调用进行.catch操作
  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

东方青云、

你的鼓励将是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值