• Promise


    这里你谈 promise的时候,除了将他解决的痛点以及常用的 API 之外,最好进行拓展把 eventloop 带进来好好讲一下,microtask(微任务)、macrotask(任务) 的执行顺序,如果看过 promise 源码,最好可以谈一谈 原生 Promise 是如何实现的。Promise 的关键点在于callback 的两个参数,一个是 resovle,一个是 reject。还有就是 Promise 的链式调用(Promise.then(),每一个 then 都是一个责任人)

    • PromiseES6 新增的语法,解决了回调地狱的问题。
    • 可以把 Promise看成一个状态机。初始是 pending 状态,可以通过函数 resolvereject,将状态转变为 resolved 或者 rejected 状态,状态一旦改变就不能再次变化。
    • then 函数会返回一个 Promise 实例,并且该返回值是一个新的实例而不是之前的实例。因为 Promise 规范规定除了 pending 状态,其他状态是不可以改变的,如果返回的是一个相同实例的话,多个 then 调用就失去意义了。 对于 then 来说,本质上可以把它看成是 flatMap

    1. Promise 的基本情况

    简单来说它就是一个容器,里面保存着某个未来才会结束的事件(通常是异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息

    一般 Promise 在执行过程中,必然会处于以下几种状态之一。

    • 待定(pending):初始状态,既没有被完成,也没有被拒绝。
    • 已完成(fulfilled):操作成功完成。
    • 已拒绝(rejected):操作失败。

    待定状态的 Promise 对象执行的话,最后要么会通过一个值完成,要么会通过一个原因被拒绝。当其中一种情况发生时,我们用 Promisethen 方法排列起来的相关处理程序就会被调用。因为最后 Promise.prototype.thenPromise.prototype.catch 方法返回的是一个 Promise, 所以它们可以继续被链式调用

    关于 Promise 的状态流转情况,有一点值得注意的是,内部状态改变之后不可逆,你需要在编程过程中加以注意。文字描述比较晦涩,我们直接通过一张图就能很清晰地看出 Promise 内部状态流转的情况

    从上图可以看出,我们最开始创建一个新的 Promise 返回给 p1 ,然后开始执行,状态是 pending,当执行 resolve之后状态就切换为 fulfilled,执行 reject 之后就变为 rejected 的状态

    2. Promise 的静态方法

    • all 方法
      • 语法: Promise.all(iterable)
      • 参数: 一个可迭代对象,如 Array
      • 描述: 此方法对于汇总多个 promise 的结果很有用,在 ES6 中可以将多个 Promise.all 异步请求并行操作,返回结果一般有下面两种情况。
        • 当所有结果成功返回时按照请求顺序返回成功结果。
        • 当其中有一个失败方法时,则进入失败方法
    • 我们来看下业务的场景,对于下面这个业务场景页面的加载,将多个请求合并到一起,用 all 来实现可能效果会更好,请看代码片段
    // 在一个页面中需要加载获取轮播列表、获取店铺列表、获取分类列表这三个操作,页面需要同时发出请求进行页面渲染,这样用 `Promise.all` 来实现,看起来更清晰、一目了然。
    
    
    //1.获取轮播数据列表
    function getBannerList(){
      return new Promise((resolve,reject)=>{
          setTimeout(function(){
            resolve('轮播数据')
          },300) 
      })
    }
    //2.获取店铺列表
    function getStoreList(){
      return new Promise((resolve,reject)=>{
        setTimeout(function(){
          resolve('店铺数据')
        },500)
      })
    }
    //3.获取分类列表
    function getCategoryList(){
      return new Promise((resolve,reject)=>{
        setTimeout(function(){
          resolve('分类数据')
        },700)
      })
    }
    function initLoad(){ 
      Promise.all([getBannerList(),getStoreList(),getCategoryList()])
      .then(res=>{
        console.log(res) 
      }).catch(err=>{
        console.log(err)
      })
    } 
    initLoad()
    
    • 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
    • allSettled 方法
      • Promise.allSettled 的语法及参数跟 Promise.all 类似,其参数接受一个 Promise 的数组,返回一个新的 Promise唯一的不同在于,执行完之后不会失败,也就是说当 Promise.allSettled 全部处理完成后,我们可以拿到每个 Promise 的状态,而不管其是否处理成功
    • 我们来看一下用 allSettled 实现的一段代码
    const resolved = Promise.resolve(2);
    const rejected = Promise.reject(-1);
    const allSettledPromise = Promise.allSettled([resolved, rejected]);
    allSettledPromise.then(function (results) {
      console.log(results);
    });
    // 返回结果:
    // [
    //    { status: 'fulfilled', value: 2 },
    //    { status: 'rejected', reason: -1 }
    // ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    从上面代码中可以看到,Promise.allSettled 最后返回的是一个数组,记录传进来的参数中每个 Promise 的返回值,这就是和 all 方法不太一样的地方。

    • any 方法
      • 语法: Promise.any(iterable)
      • 参数: iterable 可迭代的对象,例如 Array
      • 描述: any 方法返回一个 Promise,只要参数 Promise 实例有一个变成 fulfilled状态,最后 any返回的实例就会变成 fulfilled 状态;如果所有参数 Promise 实例都变成 rejected 状态,包装实例就会变成 rejected 状态。
    const resolved = Promise.resolve(2);
    const rejected = Promise.reject(-1);
    const anyPromise = Promise.any([resolved, rejected]);
    anyPromise.then(function (results) {
      console.log(results);
    });
    // 返回结果:
    // 2
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    从改造后的代码中可以看出,只要其中一个 Promise 变成 fulfilled状态,那么 any 最后就返回这个p romise。由于上面 resolved 这个 Promise 已经是 resolve 的了,故最后返回结果为 2

    • race 方法
      • 语法: Promise.race(iterable)
      • 参数: iterable 可迭代的对象,例如 Array
      • 描述: race方法返回一个 Promise,只要参数的 Promise 之中有一个实例率先改变状态,则 race 方法的返回状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给 race 方法的回调函数
    • 我们来看一下这个业务场景,对于图片的加载,特别适合用 race 方法来解决,将图片请求和超时判断放到一起,用 race 来实现图片的超时判断。请看代码片段。
    //请求某个图片资源
    function requestImg(){
      var p = new Promise(function(resolve, reject){
        var img = new Image();
        img.onload = function(){ resolve(img); }
        img.src = 'http://www.baidu.com/img/flexible/logo/pc/result.png';
      });
      return p;
    }
    //延时函数,用于给请求计时
    function timeout(){
      var p = new Promise(function(resolve, reject){
        setTimeout(function(){ reject('图片请求超时'); }, 5000);
      });
      return p;
    }
    Promise.race([requestImg(), timeout()])
    .then(function(results){
      console.log(results);
    })
    .catch(function(reason){
      console.log(reason);
    });
    
    
    // 从上面的代码中可以看出,采用 Promise 的方式来判断图片是否加载成功,也是针对 Promise.race 方法的一个比较好的业务场景
    
    • 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

    promise手写实现,面试够用版:

    function myPromise(constructor){
        let self=this;
        self.status="pending" //定义状态改变前的初始状态
        self.value=undefined;//定义状态为resolved的时候的状态
        self.reason=undefined;//定义状态为rejected的时候的状态
        function resolve(value){
            //两个==="pending",保证了状态的改变是不可逆的
           if(self.status==="pending"){
              self.value=value;
              self.status="resolved";
           }
        }
        function reject(reason){
            //两个==="pending",保证了状态的改变是不可逆的
           if(self.status==="pending"){
              self.reason=reason;
              self.status="rejected";
           }
        }
        //捕获构造异常
        try{
           constructor(resolve,reject);
        }catch(e){
           reject(e);
        }
    }
    // 定义链式调用的then方法
    myPromise.prototype.then=function(onFullfilled,onRejected){
       let self=this;
       switch(self.status){
          case "resolved":
            onFullfilled(self.value);
            break;
          case "rejected":
            onRejected(self.reason);
            break;
          default:       
       }
    }
    
    • 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
  • 相关阅读:
    前端算法之二分查找
    计算机毕设 大数据电商用户行为分析 -python 大数据
    python基础语法15-网络编程理论
    什么是SystemUI
    python元组与列表的区别
    Windows10上使用llama-recipes(LoRA)来对llama-2-7b做fine-tune
    MySQL(3)
    视频实现输入密码才能观看的应用效果
    【虹科技术分享】如何测试 DNS 服务器:DNS 性能和响应时间测试
    Elasticsearch学习系列【2】- 批量获取与批量写入文档
  • 原文地址:https://blog.csdn.net/php_martin/article/details/125856709