zoukankan      html  css  js  c++  java
  • 异步解决方案(三)Promise

    首先建议大家先看看这篇博文,这是我看过的最清晰给力的博文了:

    https://www.cnblogs.com/lvdabao/p/es6-promise-1.html

    附赠一篇笑死我了的博客,加入有一天 V8引擎重大故障,为了拯救JavaScript世界...

    https://segmentfault.com/a/1190000010765655

    Promise 是ES6中最重要的特性?

    确实如此。让回调更加优雅。

    我的第一家公司用 jQuery,第二家公司用 async/await,第三家公司使用 jQuery 和 Promise。

    比较而言,我还是最喜欢 使用 async/await,不过当时也有很多别人写的UI组件,用的是 Promise,想想,出的比ES6早,在ES6被官方认证,使用者应该还是占上风的。


    1.Promise有啥方法?

    看一下上面的博文,通过 consle.dir(Promise) -> Promise 是一个构造函数。

    • 自身拥有的方法

      • all (取时间最长的)
      • reject
      • resolve
      • race (竞赛,取时间最短的)
    • 原型链上的方法

      • catch -> async/await 中需要直接用 try .. catch 来是想,算是 ES6 优胜的一点了
      • then -> 不用 callback,像 jQuery 的 always/fail/done/then

    既然原型链上有方法,那么 new 出来,就能够获得这些方法

    let p = new Promise((resolve, reject) => {
      // 一些异步操作
      setTimeout(() => {
        console.log('执行完成');
        resolve('将数据遗留给后人');
      }, 1000);
    });
    

    可以看到,这里的 函数直接自己执行了!!! 我们只是定义了一个对象,确是自己执行。


    2.Promise 如何使用?

    So,我们需要在它外面封装一层函数。
    const Invoke = () => {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          console.log('执行完成');
          resolve({name: 'Jobs'});
        }, 2000);
      });
    };
    Invoke();
    

    通过 Invoke 函数,我们实现了 它的该出手时就出手。

    接下来,体验一下 不用 callback 地薪火相传。 无限回调地狱,再见啦。
    Invoke().then((data) => {
      console.log(data);
    });
    
    用 callback 实现一下
    const fuckPromise = (callback) => {
      setTimeout(() => {
        console.log('先实现一个小目标');
        callback({name: 'Jobs'});
      }, 1000);
    };
    
    fuckPromise((data) => {
      console.log(data);
    });
    

    对比一下: 我将他们分为 前置操作 和 后续操作。

    相同点
    • 两者的 后续操作,都作为参数被传递
    不同点
    • Promise 前置操作 中返回了一个对象,并且其中有, resolve/reject 来控制数据的传递。
    • Callback 前置操作 中则是 在内部调用了 方法参数。把数据完全交给 callback 来控制。
    • Promise 的后续操作,使用 Promise.then() 写在里面
    • Callback 的后续操作,使用 外部函数(内部函数)

    当层级一多,前者就是一堆 then,而后者??? 举个例子!!


    3.从 callback 到 Promise.

    Callback 版本1,当回调更多的时候,我们就能 很明显地感受到 还好我没用4格缩进

    let fuckCallback = () => {
      setTimeout(() => {
        console.log('峰哥峰哥,');
    
        setTimeout(() => {
          console.log('影魔王');
        }, 1000);
      }, 1000);
    }
    
    fuckCallback();
    

    Callback 版本2: 封装一下,缩进代码确实没有那么乱了,不过数据量大的时候还是不够 直观。

    let callbackOne = (callback) => {
      setTimeout(() => {
        console.log('峰哥峰哥');
        callback();
      }, 1000);
    };
    
    let callbackTwo = (callback) => {
      setTimeout(() => {
        console.log('峰哥牛逼');
        callback();
      }, 1000);
    };
    
    callbackOne(() => {
      callbackTwo(() => {
        console.log('也许我是下一个操作');
      });
    });
    

    Promise 版本: 代码没有往后延了,是的,这就是 Promise 的作用。看起来好像没卵用?只是量不够大而已。

    const stepOne = () => {
      return new Promise((resolve) => {
        setTimeout(() => {
          resolve('峰哥峰哥,');
        }, 1000);
      });
    };
    
    const stepTwo = () => {
      return new Promise((resolve) => {
        setTimeout(() => {
          resolve('影魔王!');
        }, 1000);
      });
    };
    
    stepOne()
    .then((data) => {
      console.log(data);
      return stepTwo();
    })
    .then((data) => {
      console.log(data);
    });
    

    这里,我发现了一点,其实 Promise 并不关系你如何处理数据,你一开始,只需要负责,把Promise这个传承的能力执行下去就行了。

    让我想起《我的英雄学院》这个动画里。 One for All 的能力,只需要传承即可。传承什么力量由当代决定。

    4.Promise 的其他用法

    • 直接 return 数据会怎么样?
    let wrap = () => {
      return new Promise(resolve => {
        resolve({name: 'jobs'});
      });
    };
    
    wrap()
    .then(data => {
      console.log(data);
      return data;
    })
    .then(data => {
      console.log('我再传', data);
    });
    
    • reject 和 catch 的用法
    let testReject = () => {
      return new Promise((resolve, reject) => {
        const head = Math.random();
        if (head > 0.5) {
          reject('yyf头太大了');
        } else {
          resolve('千军万马避蓝猫');
        }
      });
    }
    
    testReject()
    .then(data => console.dir(data))
    .catch(e => console.error(e));
    
    • all 的用法 (两者之中取 执行时间最长的那个 --> 同时执行,一般用来节省请求的时间)
    let p1 = () => {
      return new Promise((resolve) => {
        setTimeout(() => {
          console.log('我会执行两秒钟');
          resolve();
        }, 2000);
      });
    };
    let p2 = () => {
      return new Promise((resolve) => {
        setTimeout(() => {
          console.log('我会执行一秒钟');
          resolve();
        }, 1000);
      });
    };
    Promise
    .all([p1(), p2()])
    .then(() => console.log('他们都 resolve了,共计时 2s'));
    

    切记: Promise.all([arg1,arg2,...]) 是等里面 都 resolve() 才返回。

    没有 resolve 参数,那是不行的。 你会发现 promise.all.then 失效了。

    • race -> 竞赛,第一个 resolve 了,则进行 then 操作,第二个还是会继续执行就是了
    Promise
    .race([p1(), p2()])
    .then(() => console.log('看看过了几秒?'));
    

    总结:(引用了开篇的那个文章,抄那边的)

    ES6 Promise的内容就这些吗?是的,能用到的基本就这些。
    我怎么还见过done、finally、success、fail等,这些是啥?这些并不在Promise标准中,而是我们自己实现的语法糖

    需要继续跟进一下最顶上链接的 博客。

    • Promise/A+规范
    • jquery中的Promise

    补充一个工作中用到的 Promise,书到用时方恨少啊

    需求是这样的:
    1.首先这是两个请求,同时发送的请求,互相并不依赖
    2.当请求A返回的是 失败、且为没有资源(404) ,则进行操作 a
    3.当请求B返回的是 失败、且没有资源(404),则进行操作 b
    4.当请求A、B 返回的都是 失败、且没有资源(404),则进行操作 c
    用 Promise.all 实现,代码尽量漂亮(雾)

    我是怎么做的呢?工作中,我并没有实现它,因为当时环境出了问题,自己的技术更是有问题,于是我放在这里 写吧。(叹气一口、唉)

    1.我先去 easy-mock 创建了两个请求必须的接口

    总不能老是使用 setTimeout,有种纸上谈兵的感觉.

    Easy-mock 的配置,均为 get 请求.

    {
      "success|1": true,
      "errcode": /40[2-5]/,
      "message": "I'm the one."
    }
    
    {
      "success|1": true,
      "errcode": /40[48]/,
      "message": "I'm data2"
    }
    

    2.本地采用 fetch 的异步获取方式。越来越多的浏览器支持 fetch 了。

    fetch('https://www.easy-mock.com/mock/5ae316f1e671403cb1da46af/example/data2');
    简单地来说就是这么一句话 - 发了个请求。

    让我们看看这个网站是怎么介绍的?
    Ajax已死,Fetch永生
    可以不用看啦,一次还是只专注一个知识点就好(==),看下 fetch 的使用结构就好了 -> 返回了一个 Promise

    fetch(url).then(function(response) {
      return response.json();
    }).then(function(data) {
      console.log(data);
    }).catch(function(e) {
      console.log("Oops, error");
    });
    

    万事俱备、只欠东风。东风在下面 - -

    fetch('https://www.easy-mock.com/mock/5ae316f1e671403cb1da46af/example/data2')
    .then(res => res.json())
    .then(data => console.log(data));
    

    如果你按照我的教程没有成功。。。那么说明:

    • 你可能需要在 easy-mock 那个官网上 稍微看看,如果那边的预览没毛病,那看下一点
    • 浏览器 console 一下 fetch 看一下有没有东西, 确认下,再看下一点
    • 代码写错没?老铁!!!

    3.接下来,我们就可以完成我们的需求了。使用 Promise.all.想了想,还是先练习一下。

    练习版本: 突然发现好简单,之前主要疑惑的地方还是在 all 方法怎么传递数据的地方。

    当我明白了 all 之后的回调里,自带 resolve 里面的数据的时候,就觉得好简单了。

    let arr = [404, 200];
    let p1 = () => new Promise((resolve, reject) => {
      setTimeout(() => {
        let index = Math.round(Math.random());
        let res = arr[index];
    
        console.log('2秒后,res1:', res);
        if (res === 404) {
          resolve(404);
        } else if (res === 200) {
          resolve();
        }
      }, 2000);
    });
    
    let p2 = () => new Promise((resolve, reject) => {
      setTimeout(() => {
        let index = Math.round(Math.random());
        let res = arr[index];
    
        console.log('3秒后, res2:', res);
        if (res = 404) {
          resolve(404);
        } else if (res === 200) {
          resolve();
        }
    
      }, 3000);
    });
    
    Promise.all([p1(), p2()])
    .then((data) => {
      console.log(data);
    })
    .catch(() => {});
    

    实战版本: 还是比较不一样的,我在这里请求了百度并且出错了,所以需要注意 resolve 不必要的判断(如果你的逻辑是这样的话,这是前提)。

    let p1 = () => new Promise((resolve, reject) => {
      fetch('https://www.easy-mock.com/mock/5ae316f1e671403cb1da46af/example/data2')
      .then(res => res.json())
      .then(data => resolve(data && data.errcode || null))
      .catch(() => resolve());
    });
    
    let p2 = () => new Promise((resolve, reject) => {
      fetch('https://www.easy-mock.com/mock/5ae316f1e671403cb1da46af/example/data')
      .then(res => res.json())
      .then(data => resolve(data && data.errcode || null))
      .catch(() => resolve());
    });
    
    Promise.all([p1(), p2()])
    .then((data) => {
      console.log(data);
      if (data[0] === 404 || data[1] === 404) {
        console.log('服务器资源不存在的逻辑');
      } else {
        return;
      }
    });
    // 注意,不要在最后的 all 里再判断 catch,比较好的方式 应该是在 p1/p2 就判断好。
    

    补充一个描述中的模仿 Promise 的结构. --- 所谓的组合构造模式,没想到在这里也遇见了。

    function Promise(excutor) {
      this.PromiseStatus; // 共有三种状态 pending/fulfilled/rejected(等待/完成/拒绝 状态)
      this.PromiseValue; // 用于存储数据
      var resolve = function(res) {};// 闭包函数
      var reject = function(res) {}; // 闭包函数
      executor(resolve, reject);  // new 一个新的 Promise 会传递一件你需要做的事情
    }
    Promise.prototype.then = function() {};
    Promise.prototype.catch = function() {};
    Promise.prototype.chain = function() {};
    

    上面的这个,有助于我们 理解 Promise,跟在 Chrome 打出来的东西实质上是一样的。


    各种方法的 Polyfill/源码

    https://www.cnblogs.com/fsjohnhuang/p/4135149.html#a623

    因为自己在看 Promise.all 方法的 polyfill 的时候遇见了些困难,这些摘抄下网上关键的提示 --- Promise.all 的特点

    • 接受一个 iterator 参数(可迭代的迭代器)
    • 如果元素不是 Promise 格式的,则通过 Promise.resolve 转化弄成 Promise

    非我族类,必存异心

    • 如果全部成功,状态变为 resolve,返回值组成一个数组传递给回调
    • 只要有一个失败,返回值将直接回调

    其实,就我当前的了解:其实所谓的 Promise.all 就是 给外层 多加了一层 Promise

    人民的统一,需要一个共同的 人/机构。 Promise 不外如此。

    终于看懂了官网的 Promise.all,一些注释的记录。

      let p1 = () => new Promise((resolve, reject) => {
        fetch('https://www.easy-mock.com/mock/5ae316f1e671403cb1da46af/example/data')
        .then(res => res.json())
        .then(data => console.log(data))
        .catch(e => console.log(e));
      });
    
      let p2 = () => new Promise((resolve, reject) => {
        fetch('https://www.easy-mock.com/mock/5ae316f1e671403cb1da46af/example/data2')
        .then(res => res.json())
        .then(data => console.log(data))
        .catch(e => console.log(e));
      });
    
      // polyfill start
    
      Promise.all = function(arr) {
        var args = Array.prototype.slice.call(arr);
    
        return new Promise(function (resolve, reject) {
          // 若传进来的数组为 0
          if (args.length === 0) return resolve([]);
          // 这里出现了计数的 数组长度 - 所有的 Promise 长度
          var remaining = args.length;
    
    
          function res(i, val) {
            // 某种程度上,说明了 val 是一个Promise对象
            if (val && (typeof val === 'object')) {
              var then = val.then;
              // 确认一下是不是 promise 中的 then 函数
              if (typeof then === 'function') {
                var p = new Promise(then.bind(val));
                p.then(function(val) {
                  res(i, val);
                }, reject);
                return;
              }
            }
    
            // 跳到这里来的有两种:
            // A.不是Promise对象,直接跳过来
            // B.执行过后的Promise对象,Promise要在上面的地方多执行一次,返回来的数据就是
            args[i] = val;
            if (--remaining === 0) {
              // 这里就是结果数组了吧
              resolve(args);
            }
          }
    
    
    
          for(var i = 0; i < args.length; i++) {
            res(i, args[i]);
          }
        })
      }
    
      Promise.all([p1(), p2()])
      .then();
    
  • 相关阅读:
    android 颜色值参考,(有颜色图
    Virtual address cache memory, processor and multiprocessor
    VUEJS2.0源码理解--优
    qlserver、Mysql、Oracle三种数据库的优缺点总结
    三层与MVC
    数据结构 常用定义
    c语言 typedef
    C动态内存分配
    c 指针(一)
    stdlib 头文件
  • 原文地址:https://www.cnblogs.com/can-i-do/p/8948436.html
Copyright © 2011-2022 走看看