• 手撕代码彻底理解Promise


    一、异步

    1. js是单线程的,只能同步进行,优化用户体验,解决该问题产生了异步的操作
    2. 早先异步的解决方案,回调函数(callback)

    二、回调函数的问题

    1. 错误处理困难
    2. 有依赖的执行回调造成回调地狱
    3. 无依赖的并行回调执行代码很冗余

    解释一 :错误处理困难

    getJson('api/a.json',function(){})
    
    // 若上面的函数发生错误 我们无法使用try catch 捕获错误
    // 不能这样使用
    try{
      getJson('api/a.json',function(){})
    }catch(err=>{
      console.log(err)
    })
    // 原因 是回调函数和开始任务中这段代码不在同一个事件循环步骤
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    解释二:有依赖的执行回调造成回调地狱

    这个很常见,比如一个场景,我们需要先拿到用户信息调用a接口,在用户信息的基础上我们需要获取用户的课程调用b接口,拿到用户的课程后我们需要判断该课程是否过期调用c接口

    回调地狱产生

    解释三:无依赖的并行回调执行代码很冗余

    比如 我们需要从三个接口分别获取三份数据,然后在某个方法中同时使用这个三个数据,

    由于我们不知道,数据返回的先后,只能分别在获取三分数据的接口中去调用封装好的函数去同时使用这三份数据。

    三、promise登场

    promise解释

    可以简单的理解为对于异步任务结果的一个占位符。我现在还没拿到数据,但是我将来会得到。

    promise生命周期

    1. pending(等待阶段 未完成状态
    2. fulfilled (resolve函数调用 已完成状态
    3. rejected (reject函数被调用 已完成状态

    promise的执行顺序

    DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="UTF-8" />
            <meta name="viewport" content="width=device-width, initial-scale=1.0" />
            <title>promise执行顺序title>
        head>
        <body>
            <script>
                console.log('start code');
                const aPromise = new Promise((resolve, reject) => {
                    console.log('进入a promise');
                    // 这里模拟一个异步请求
                    setTimeout(() => {
                        console.log('进入a promise 定时器');
                        resolve('a promise 成功');
                    }, 500);
                });
    
    
    
                aPromise
                    .then((res) => {
                        console.log(res);
                    })
                    .catch((err) => {
                        console.log(err);
                    });
    
    
    
                const bPromise = new Promise((resolve, reject) => {
                    console.log('进入b promise');
                    resolve('b promise 成功');
                });
    
    
    
                bPromise
                    .then((res) => {
                        console.log(res);
                    })
                    .catch((err) => {
                        console.log(err);
                    });
    
    
    
                console.log('end code');
            script>
        body>
    html>
    结果
    start code
    pro.html:12 进入a promise
    pro.html:29 进入b promise
    pro.html:41 end code
    pro.html:35 b promise 成功
    pro.html:15 进入a promise 定时器
    pro.html:22 a promise 成功
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    显示拒绝和隐式拒绝

    显示拒绝是我们手动调用reject函数

    隐式拒绝是业务代码发生异常错误

    四、promise链式调用 解决回调地狱

    相对callback会好一些 但是还不是最完美方案(后面的aysnc/await 是同步书写的执行异步方法的完美方案)

    getJson('api/a.json')
    .then(user=>getJson(user[0].courseUrl))
    .then(course=>getJson(canuseUrl))
    .catch(e=>{
      console.log(e)
    }))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    五、Promise.all

    当所有的子Promise都完成,该Promise完成,返回值是全部值的数组。

    如果有任何一个失败,该Promise失败,返回值是第一个失败的子Promise的结果。

    Promise.all([getJson('api/a,json'),
    getJson('api/b,json'),
    getJson('api/c,json')]).then(res=>{
      const a = res[0];
      const b = res[1];
      const c = res[2];
    }).catch(e=>{
      console.log(e)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    六、Promise.race

    它有任意一个返回成功后,就算完成,但是 进程不会立即停止

    Promise.race([getJson('api/a,json'),
    getJson('api/b,json'),
    getJson('api/c,json')]).then(res=>{
      if(res !== null){
        //执行操作
      }
    }).catch(e=>{
      console.log(e)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    场景:利用Promise.race 做请求超时处理

    把异步操作和定时器放到一起,如果定时器先触发,认为超时,告知用户

    let p1 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('成功了')
      }, 2000);
    })
    let p2 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('success')    
      }, 5000);
    })
    Promise.race([p1, p2]).then((result) => {
      console.log(result)               //['成功了', 'success']
    }).catch((error) => {
      console.log(error)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    七、promise封装异步请求

    DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="UTF-8" />
            <meta name="viewport" content="width=device-width, initial-scale=1.0" />
            <title>promise 封装异步请求title>
        head>
        <body>
            <script>
                function getData(url) {
                    const promise = new Promise((resolve, reject) => {
                        const xhr = new XMLHttpRequest();
    
    
    
                        xhr.open('GET', url);
                        xhr.onload = function () {
                            try {
                                if (xhr.status === 200) {
                                    console.log(xhr.responseText);
                                    resolve(xhr.responseText);
                                } else {
                                    reject(xhr.status);
                                }
                            } catch (error) {
                                reject(error.status);
                            }
                        };
                        xhr.onerror = function () {
                            reject(xhr.status);
                        };
                        xhr.send(null);
                    });
    
    
    
                    return promise;
                }
    
    
    
                let url = 'http://127.0.0.1:8881';
                getData(url)
                    .then((res) => {
                        const data = JSON.parse(res);
                        console.log(data.a);
                        return data;
                    })
                    // 注意这里的链式调用 需要上面的then 返回值才行
                    .then((data) => {
                        console.log(data);
                    })
                    .catch((err) => {
                        console.log(err);
                    });
            script>
        body>
    html>
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    八、 Promise实现原理

    /**
                 * promsie 主要有7个属性
                 * state 状态
                 * value 成功返回值
                 * reson 错误信息
                 * resolve 方法 执行成功调用
                 * reject 方法 执行失败调用
                 * then 方法
                 * **/
                // 简单版本
                function myPromise(constructor) {
                    let self = this;
                    self.status = 'pending';
                    self.value = undefined;
                    self.reason = undefined;
                    function resolve(value) {
                        if (self.status === 'pending') {
                            self.value = value;
                            self.status = 'fulfilled';
                        }
                    }
    
    
    
                    function reject(reason) {
                        if (self.status === 'pending') {
                            self.value = reason;
                            self.status = 'rejected';
                        }
                    }
    
    
    
                    // 捕获构造异常
                    try {
                        constructor(resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                }
    
    
    
                myPromise.prototype.then = function (onFulfilled, onRejected) {
                    let self = this;
                    switch (self.status) {
                        case 'fulfilled':
                            onFulfilled(self.value);
                            break;
                        case 'rejected':
                            onRejected(self.reason);
                            break;
                        default:
                    }
                };
    
    
    
                // 测试
                let p = new myPromise((resolve, reject) => {
                    resolve(1);
                });
                p.then((res) => {
                    console.log(res);
                });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    参考:

    1. 《javascript 忍着密术 第二版》P145-157
    2. Promise实现原理
  • 相关阅读:
    流程引擎-自定义函数的应用
    最长公共字符串后缀
    GaussDB数据库SQL系列-数据去重
    揭秘,Vue3 性能优化之 Non-reactive Object
    【算法题】LeetCode691、贴纸拼词(剪枝+记忆化搜索)
    MySQL十种锁,一篇文章带你全解析
    STM32单片机蓝牙APP智能鱼缸水位温度加氧定时喂食补光控制系统
    Spring Boot 中单元测试框架 Mockito 的正确使用
    CSS自适应页面
    壹资源知识付费系统源码-小程序端+pc端
  • 原文地址:https://blog.csdn.net/Hreticent/article/details/127787575