对于promise的常见面试题总结分析

1、了解 Promise 吗?

  • 了解Promise,Promise是一种异步编程的解决方案,有三种状态,pending(进行中)、resolved(已完成)、rejected(已失败)。当Promise的状态由pending转变为resolved或reject时,会执行相应的方法
  • Promised的特点是只有异步操作的结果,可以决定当前是哪一种状态,任务其他操作都无法改变这个状态,也是“Promise”的名称的由来,同时,状态一旦改变,就无法再次改变状态

2、Promise 解决的痛点是什么?

Promise解决的痛点:

  • 回调地狱,代码难以维护, 常常第一个的函数的输出是第二个函数的输入这种现象,是为解决异步操作函数里的嵌套回调(callback hell)问题,代码臃肿,可读性差,只能在回调里处理异常

  • promise可以支持多个并发的请求,获取并发请求中的数据

  • promise可以解决可读性的问题,异步的嵌套带来的可读性的问题,它是由异步的运行机制引起的,这样的代码读起来会非常吃力

  • promise可以解决信任问题,对于回调过早、回调过晚或没有调用和回调次数太少或太多,由于promise只能决议一次,决议值只能有一个,决议之后无法改变,任何then中的回调也只会被调用一次,所以这就保证了Promise可以解决信任问题

3、Promise 解决的痛点还有其他方法可以解决吗?如果有,请列举。

  • Promise 解决的痛点还有其他方法可以解决,比如setTimeout、事件监听、回调函数、Generator函数,async/await

  • setTimeout:缺点不精确,只是确保在一定时间后加入到任务队列,并不保证立马执行。只有执行引擎栈中的代码执行完毕,主线程才会去读取任务队列

  • 事件监听:任务的执行不取决于代码的顺序,而取决于某个事件是否发生

  • Generator函数虽然将异步操作表示得很简洁,但是流程管理却不方便(即何时执行第一阶段、何时执行第二阶段)。即如何实现自动化的流程管理

  • async/await

4、Promise 如何使用?

  • 创造一个Promise实例
  • Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数
  • 可用Promise的try和catch方法预防异常

5、Promise 常用的方法有哪些?它们的作用是什么?

Promise.resolve(value)

类方法,该方法返回一个以 value 值解析后的 Promise 对象
如果这个值是个 thenable(即带有 then 方法),返回的 Promise 对象会“跟随”这个 thenable 的对象,采用它的最终状态(指 resolved/rejected/pending/settled
如果传入的 value 本身就是 Promise 对象,则该对象作为 Promise.resolve 方法的返回值返回
其他情况以该值为成功状态返回一个 Promise 对象

Promise.reject

类方法,且与 resolve 唯一的不同是,返回的 promise 对象的状态为 rejected

Promise.prototype.then

实例方法,为 Promise 注册回调函数,函数形式:fn(vlaue){}value 是上一个任务的返回结果,then 中的函数一定要 return 一个结果或者一个新的 Promise 对象,才可以让之后的then 回调接收

Promise.prototype.catch

实例方法,捕获异常,函数形式:fn(err){}, err 是 catch 注册 之前的回调抛出的异常信息

Promise.race

类方法,多个 Promise 任务同时执行,返回最先执行结束的 Promise 任务的结果,不管这个 Promise 结果是成功还是失败

Promise.all

类方法,多个 Promise 任务同时执行
如果全部成功执行,则以数组的方式返回所有 Promise 任务的执行结果。 如果有一个 Promise 任务 rejected,则只返回 rejected 任务的结果

6、Promise 在事件循环中的执行过程是怎样的?

事件循环

  • 从代码执行顺序的角度来看,程序最开始是按代码顺序执行代码的,遇到同步任务,立刻执行;遇到异步任务,则只是调用异步函数发起异步请求。此时,异步任务开始执行异步操作,执行完成后到消息队列中排队。程序按照代码顺序执行完毕后,查询消息队列中是否有等待的消息。如果有,则按照次序从消息队列中把消息放到执行栈中执行。执行完毕后,再从消息队列中获取消息,再执行,不断重复。由于主线程不断的重复获得消息、执行消息、再取消息、再执行

promise的事件循环

  • Promise在初始化时,传入的函数是同步执行的,然后注册 then 回调。注册完之后,继续往下执行同步代码,在这之前,then 中回调不会执行。同步代码块执行完毕后,才会在事件循环中检测是否有可用的 promise 回调,如果有,那么执行,如果没有,继续下一个事件循环

7、Promise 的业界实现都有哪些?

  • promise可以支持多个并发的请求,获取并发请求中的数据

  • promise可以解决可读性的问题,异步的嵌套带来的可读性的问题,它是由异步的运行机制引起的,这样的代码读起来会非常吃力

8、能不能手写一个 Promise 的 polyfill?

  • Promsie的 polyfill 的源码实现
    实例代码:
function Promise(fn){
        //  说明 Promise必须以构造函数形式被调用
        if(!(this instanceof  Promise))
            throw  new TypeError('Promises must be constructed via new');
        // 说明 Promise的唯一参数fn必须是函数类型
        if(typeof fn !== "function")
            throw new TypeError('not a function');
        // _state属性定义了Promise的状态
        // Promise有pending、fulfilled、rejected三种状态,分别对应_state值为0、1、2
        this._state = 0;
        // _handled属性的类型为Boolean,初始值为false,其代表Promise是否被处理
        this._handled = false;
        //  _value属性的类型为Promise或undefined,初始值为undefined
        this._value = undefined;
        // _deferreds属性的类型为Array,初始值为空数组,数组中存放的值为Function
        this._deferreds = [];
        // 将Promise的参数fn与代表当前对象的this作为参数,deResolve函数进行调用
        doResolve(fn,this);
    }

function doResolve(fn, self) {
        // done变量的作用就是为了防止resolve()和reject()被同时调用
        //  Promise的状态只能从pending->fulfilled或pending->rejected
        var done = false;
        try {
            //  fn的构造函数的参数,new Promise传入的回调函数  fn(resolve,reject);
            fn(
                    function(value) {
                        if (done) return;
                        // done变量为true则直接退出函数
                        done = true;
                        resolve(self, value);
                    },
                    function(reason) {
                        if (done) return;
                        done = true;
                        reject(self, reason);
                    }
            );
        } catch (ex) {
            // 调用Promsie构造函数如果抛出异常,则Promise就会变为rejected状态
            if (done) return;
            done = true;
            reject(self, ex);
        }
}
  • Promise 的 polyfill的实现
    实例代码:
 const promiseStatusSymbol = Symbol("PromiseStatus");
    const promiseValueSymbol = Symbol("PromiseValue");
    // pending、fulfilled、rejected
    const status = {
        pending:"pending",
        fulfilled:"fulfilled",
        rejected:"rejected"
    };
    const transition = function(status){
    /*    var self = this;
        return function(value){
            this[promiseStatusSymbol] = status;
            this[promiseValueSymbol] = value;
        }*/

       return (value) => {
            this[promiseValueSymbol] = value;
            setStatus.call(this,status);
        }
    };

    // 对于状态的改变进行控制
    // 如果状态从 pending 到 fulfilled, 那么调用链式的下一个fulfilled函数
    // 如果状态从 pending 到 rejected, 那么调用链式的下一个rejected函数
    const setStatus = function(status){
       this[promiseStatusSymbol] = status;
       if(status === status.fulfilled){
           this.deps.resolver && this.deps.resolver();
       }else if( status === status.rejected){
           this.deps.rejector && this.deps.rejector();
       }
    };

    // 当开始异步操作的时候,还没有结果的时候,处于pending状态,然后再改变为成功或者是失败的状态
    const promise = function(resolver){
      if(typeof  resolver !== "function"){
          throw new TypeError("parameter 1 must be a function");
      }
        this[promiseStatusSymbol] = status.pending;
        this[promiseValueSymbol] = [];
        this.deps = [];
        resolver(
            //  返回函数resolve和reject
            //  这两个函数会分别对当期的Promise的状态和值进行修改,修改成功或者失败
            transition.call(this,status.fulfilled),
            transition.call(this.status.rejected)
        );
    };

    // promise的链式调用主要是一个对于依赖进行依次收集的过程,then方法是添加依赖,不是执行回调函数
    promise.prototype.then = function(fulfilled,rejected){
        const self = this;
        return promise(function(resolve,reject){
           const callback = function () {
               // 回调函数执行的返回值需要保存下来
               // 在链式调用的时候,参数应该传递给链式调用的下一个
               const resoleValue = fulfilled(self[promiseValueSymbol]);
              // resolve(resoleValue);
              // 返回值相当于是一个thenable对象,改变直接调用then方法,获取一个返回值
              if(resoleValue && typeof resoleValue.then === "function"){
                  // 内嵌promise,将得到的值绑定在promise的依赖中
                  resoleValue.then(function(data){
                      resolve(data);
                  },function(err){
                      reject(err);
                  });
              }else{
                  // then 方法链式调用的连接点
                  // 在初始化状态或者上一次promise的状态发生改变的时候,调用当前promise成功的方法
                  //  对当前promise的状态进行改变,以及进行调用链式的下一个promise的回调
                  resolve(resoleValue);
              }
           };
           const errCallback = function(){
               const rejectValue = rejected(self[promiseValueSymbol]);
               reject(rejectValue);
           };

           // 对于promise的状态处理
           // 如果上一个promise在执行then方法之前就已经完成了,那么下一个promise对应的回调应该立即执行
           // 如果当前的状态为pending,说明promise的异步操作还没有决议,
          //成功和失败的回调应该保存在之前的promise的依赖之中
            if(self[promiseStatusSymbol] === status.fulfilled){
               return callback();
           }else if(self[promiseStatusSymbol] === status.rejected){
               return errCallback();
           }else if(self[promiseStatusSymbol] === status.pending){
               self.deps.resolver = callback;
               self.deps.rejector = errCallback;
           }
        });
    };

9、 Promise的问题?解决办法?

promise的问题为:

  • promise一旦执行,无法中途取消

  • promise的错误无法在外部被捕捉到,只能在内部进行预判处理

  • promise的内如何执行,监测起来很难

解决办法

  • 正是因为这些原因,ES7引入了更加灵活多变的async,await来处理异步

10、手写Promise与ajax的结合?

实例代码:

function promiseGet (url) {
  return new Promise((resolve, reject) => {
    let xhr = new XMLHttpRequest()
    xhr.open('GET', url, true)
    xhr.onreadystatechange = function () {
      if (this.readyState === 4) {
        if (this.status === 200) {
          resolve(this.responseText,this)
        } else {
          let resJson = {
            code: this.status,
            response: this.response
          }
          reject(resJson, this)
        }
      }
    }
  })

作者:旧城tk
链接:https://juejin.im/post/5d10dfeb6fb9a07ecc44921c

求点赞,求关注~


©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容

  • Promise 对象 Promise 的含义 Promise 是异步编程的一种解决方案,比传统的解决方案——回调函...
    neromous阅读 8,698评论 1 56
  • 目录:Promise 的含义基本用法Promise.prototype.then()Promise.prototy...
    BluesCurry阅读 1,488评论 0 8
  • Promiese 简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果,语法上说,Pr...
    雨飞飞雨阅读 3,348评论 0 19
  • 你不知道JS:异步 第三章:Promises 在第二章,我们指出了采用回调来表达异步和管理并发时的两种主要不足:缺...
    purple_force阅读 2,050评论 0 4
  • 弄懂js异步 讲异步之前,我们必须掌握一个基础知识-event-loop。 我们知道JavaScript的一大特点...
    DCbryant阅读 2,693评论 0 5