promise

46 阅读7分钟

promise

  • 其实 promise 就可以理解为一个盒子, 这个盒子内部 可以帮我们承载一些 需要一定时间才能加载完毕的代码 (其实就是异步代码)

  • 刚书写一个 promise 内部的状态为 等待/持续

  • 将来promise 会转变状态, 要么转为成功 要么转为失败

    • 而且 promise 的状态一经转换, 永远不会在改变
    • 等待 -> 成功
    • 等待 -> 失败
  • 利用 ES6 新增的一个内置构造函数 帮助我们创建

 const p = new Promise(function (reslove, rejected) {
        // * 当前回调函数 会接收两个形参
        // *  第一个形参: 他的值是一个函数, 你可以调用, 调用完毕后, 可以修改 当前 promise 的状态为成功
        // *  第二个形参: 他的值也是一个函数, 你也可以调用, 调用完毕后, 可以修改 当前 promise 的状态为失败
        // * 
        // *  注意: 两个形参都是一个函数, 函数由 Promise 处理, 我们不需要处理, 我们只需要在合适的时机 调用即可
 })
  • promise 实例化对象中, 有一个 方法 then 是一个函数

    • 当前的 then 内部可以接收一个 回调函数
  • 这个回调函数会在当前的 promise 实例化对象, 状态为成功的时候执行

  • 还有另外一个方法 catch 也是一个函数, 内部可以接受一个 回调函数

    • 这个回调函数会在当前的 promise 实例化对象, 状态为失败的时候执行
  • 注意:

    • 不管是 then 函数内部的 回调函数还是 catch 函数内部的回调函数

      • 都可以接受一个参数
    • 这个参数的值由在 promise 内部更改状态的函数 传递

    • 这个参数也不是必须要有, 你需要的话可以用, 不需要可以不写

    p.then((res) => {
            console.log('当前的 promise 实例化对象 如果状态成功, 那么我会执行', res)
        }).catch((err) => {
            console.log('当前的 promise 实例化对象 如果状态失败, 那么我会执行', err)
        })
 function fn() {
            return new Promise(function (reslove, rejected) {
                const time = Math.floor(Math.random() * 3000) + 2000
                setTimeout(() => {
                    if (time > 3000) {
                        rejected(time)
                    } else {
                        reslove(time)
                    }
                }, time)
            })
        }
   // 链式调用, 在一个 then 后 返回一个 新的 promise 实例化对象, 那么我们可以再添加一个 then 函数
        p.then((res) => {
            console.log('成功', res)
            return fn()
        }).then((res) => {
            console.log('如果第二次成功, 那么我也会执行')

            return fn()
        }).then((res) => {
            console.log('如果第三次成功, 那么我也会执行')
        }).catch((err) => {
            console.log('失败', err)
        })

  • Promise就是一个用来存储数据对象

  • 但是由于Promise存取的方式的特殊,所以可以直接将异步调用的结果存储到Promise中

  • 对Promise进行链式调用时

  • 后边的方法(then和catch)读取的上一步的执行结果

  • 如果上一步的执行结果不是当前想要的结果,则跳过当前的方法

  • 当Promise出现异常时,而整个调用链中没有出现catch,则异常会向外抛出

async 和 await

  • 必须要结合 promise 一起使用

  • async 书写在一个函数的前边, 表明当前是一个异步函数

  • await 书写在 promise 实例化对象前, 有一个作用是 只有当前这个对象的状态确定, 才会往下一行运行

  • 注意:

    • async await 只能处理 promise 的成功状态, 不能处理失败状态
    
        function post() {
            return new Promise(function (reslove, rejected) {
                const time = Math.floor(Math.random() * 3000) + 2000
                setTimeout(() => {
                    if (time > 30) {
                        rejected(time)
                    } else {
                        reslove(time)
                    }
                }, time)
            })
        }
    
    
        // 原本的写法
        // fn().then((res) => {
        //     console.log(res, '成功')
        // }).catch((err) => {
        //     console.log(err, '失败')
        // })
    
        // 利用 async await 优化
        async function fn () {
            const res = await post()
            console.log('第一次请求完毕的结果: ', res)
    
            const res_2 = await post()
            console.log('第二次请求完毕的结果: ', res_2)
    
            console.log('一定是上边的异步代码运行完毕后, 才会执行我这个代码')
        }
    
        fn()
    
  • 通过await调用异步代码时,需要通过try-catch来处理异常

  function post() {
            return new Promise(function (reslove, rejected) {
                const time = Math.floor(Math.random() * 3000) + 2000
                setTimeout(() => {
                    if (time > 3000) {
                        rejected('当前请求超时, 目前的时间是' + time)
                    } else {
                        reslove(time)
                    }
                }, time)
            })
        }

        async function fn() {

            // 当前写法 不能处理 promise 的错误状态
            // const res = await post()
            // console.log('第一次请求完毕的结果: ', res)

            /**
             *  解决方案
             *      推荐做法
            */
            try {
                // 如果当前分支的代码, 运行完毕没有报错, 那么直接结束
                const res = await post()
                console.log('第一次请求完毕的结果: ', res)

                // 如果当前分支的代码运行完毕后, 有报错, 那么会阻断报错, 并且将错误信息, 传递给 catch 分支的形参
            } catch (err) {
                console.log(err, '如果我执行, 说明 第一次请求出现报错')
            }


            try {
                // 如果当前分支的代码, 运行完毕没有报错, 那么直接结束
                const res = await post()
                console.log('第二次请求完毕的结果: ', res)

                // 如果当前分支的代码运行完毕后, 有报错, 那么会阻断报错, 并且将错误信息, 传递给 catch 分支的形参
            } catch (err) {
                console.log(err, '如果我执行, 说明 第二次请求出现报错')
            }
        }

        fn()

function sum(a, b) {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(a + b)
        }, 2000);
    })
}
async function fn3() {   try {
        let result = await sum(123, 456)
        result = await sum(result, 8)
        result = await sum(result, 9)
        console.log(result)
    } catch (e) {
        console.log(e,"出错了~~")
    }

}
    *  async await 只能处理 promise 的成功状态, 不能处理失败状态

         *  我们 async await 解决不了 promise 的失败状态

         *  所以我们干脆将 promise 修改成 一定是成功状态

         *  但是如果将 promise 修改成一定成功, 也会出现一个新的问题
         * 
         *  我们在外部没有办法确定当前是成功还是失败

         *      所以有一个 前端内部所有人的约定
         *          我们在返回信息的时候, 通过一个对象返回
         *              对象内部可以有多个属性, 其中有一个 属性叫做 code
         * 
         *              如果 code === 0 代表失败
         *                  code === 1  代表成功
        */

        function post() {
            return new Promise(function (reslove, rejected) {
                const time = Math.floor(Math.random() * 3000) + 2000
                setTimeout(() => {
                    if (time > 3000) {
                        // rejected('当前请求超时, 目前的时间是' + time)
                        reslove({
                            code: 0,
                            data: '当前请求超时, 目前的时间是' + time
                        })
                    } else {
                        reslove({
                            code: 1,
                            data: time
                        })
                    }
                }, time)
            })
        }

        async function fn() {
            const res = await post()
            console.log('请求完毕的结果: ', res)

            if (res.code === 0) {
                console.log('当前的请求失败了, 做一点措施, 并且不要再往下进行了')

                return alert(res.data)
            }

            console.log('当前请求成功, 拿到数据后我们开始渲染页面')
        }

        fn()

promise的其他方法

 Promise.resolve() 创建一个立即完成的Promise
    Promise.reject() 创建一个立即拒绝的Promise
    Promise.all([...]) 同时返回多个Promise的执行结果
        其中有一个报错,就返回错误
    Promise.allSettled([...]) 同时返回多个Promise的执行结果(无论成功或失败)
       {status: 'fulfilled', value: 579}
       {status: 'rejected', reason: '哈哈'}
    Promise.race([...]) 返回执行最快的Promise(不考虑对错)
    Promise.any([...]) 返回执行最快的完成的Promise   如果所有的Promise都失败才会返回一个错误信息。
        // 构造函数上的方法

        // 返回一个状态为 成功的 promise 实例化对象
        Promise.resolve().then(() => {
            console.log('一定执行 then')
        }).catch(() => {
            console.log('我不会执行')
        })

        Promise.resolve('随手传入一个参数').then((res) => {
            console.log('一定执行 then', res)
        })


        // 返回一个状态为 失败的 promise 实例化对象
        Promise.reject().then(() => {
            console.log('我不会执行')
        }).catch(() => {
            console.log('我一定会执行')
        })

        Promise.reject('传入一个字符串 作为参数').catch(err => console.log(err))
    //. 实例化对象上的方法
        Promise.all([fn(), fn(), fn()]).then(() => {
            // 数组中所有的 promise 实例化对象 全都成功的时候执行
            console.log('成功')
        }).catch(() => {
            // 数组中 所有的 peomise 实例化对象, 有一个失败, 就执行
            console.log('失败')
        })


       /**
         *  当前方法接受一个数组, 数组内可以传入多个 promise 实例化对象
         * 
         *  等全部执行完毕的时候, 会执行后续的 then 方法
         * 
         *  then 方法接受一个参数, 就是我们传入所有 promise 实例化对象运行的结果
        */
        Promise.allSettled([fn(), fn(), fn()]).then((res) => {
            console.log('执行结束', res)
        }).catch(() => {
            console.log('你看我会不会执行')
        })

      // race 会接收一个数组, 数组中可以传入若干个 promise 实例化对象, 其中状态出现最快的会影响当前的结果
        Promise.race([fn(), fn(), fn()]).then(() => {
            // 如果第一个出现结果的 promise实例化对象为成功, 就执行
            console.log('成功')
        }).catch(() => {
            // 如果第一个出现结果的 promise实例化对象为失败, 就执行
            console.log('失败')
        })

         Promise.any([
            Promise.reject(1111),
            Promise.reject(2222),
            Promise.reject(3333),
        ]).then(r => {
            console.log(r)
        }).catch(r => {
            console.log("错误", r)
        })

        fn()
            .then(() => { console.log('成功') })
            .catch(() => { console.log('失败') })
            .finally(() => { console.log('不管结果成功还是失败, 都会执行') })

promise 内的 异步代码

    1. new Promise 内部 是按照 同步的顺序执行, 除非你写了异步代码 (定时器/请求相关的)
    1. promise 的实例化对象中的方法 (then/catch/finally) 这三个代码是按照异步任务执行
  • JS 代码运行流程, 从上往下运行

    • 如果有异步任务
      • 先执行一个 宏任务 (是把 JS 整体代码当作一个 宏任务)

      • 然后在执行 微任务队列的清空操作