• Promise 面试题整理


    基础输出题

    • 题目1
    const promise = new Promise((resolve, reject) => {
        console.log(1)
        resolve()
        console.log(2)
    })
    promise.then(() => {
        console.log(3)
    })
    console.log(4)
    

    记住 new Promise 里的参数函数,是同步被执行的,故而先输出 1,2.

    resolve 后还需要等待进入下一个事件循环。then 把参数函数推入微任务队列,并不直接执行。

    输出 4,接着事件循环进入下一轮,输出 3.

    1
    2
    4
    3
    
    • 题目2

    来自网易。给出一个 promise

    var promise = new Promise(function(resolve, reject){
      setTimeout(function() {
        resolve(1);
      }, 3000)
    })
    

    请问这三种有何不同?

    // 1
    promise.then(() => {
      return Promise.resolve(2);
    }).then((n) => {
      console.log(n)
    });
    
    // 2
    promise.then(() => {
      return 2
    }).then((n) => {
      console.log(n)
    });
    
    // 3
    promise.then(2).then((n) => {
      console.log(n)
    });
    
    1. 输出2。Promise.resolve 就是一个 Promise 对象就相当于返回了一个新的 Promise 对象。然后在下一个事件循环里才会去执行 then
    2. 输出2。和上一点不一样的是,它不用等下一个事件循环。
    3. 输出1。then 和 catch 期望接收函数做参数,如果非函数就会发生 Promise 穿透现象,打印的是上一个 Promise 的返回。
    • 题3

    来自快手的变态题目,好讨厌。得配合事件循环机制来看

    let a;
    const b = new Promise((resolve, reject) => {
      console.log('promise1');
      resolve();
    }).then(() => {
      console.log('promise2');
    }).then(() => {
      console.log('promise3');
    }).then(() => {
      console.log('promise4');
    });
    
    a = new Promise(async (resolve, reject) => {
      console.log(a);
      await b;
      console.log(a);
      console.log('after1');
      await a
      resolve(true);
      console.log('after2');
    });
    
    console.log('end');
    

    我直到现在也没有想明白其中的一步,先贴答案

    promise1
    undefined
    end
    promise2
    promise3
    promise4
    Promise { pending }
    after1
    

    第一个输出 promise1,是因为 Promise 里的方法立即执行。接着调用 resolve,只不过 then 里的方法等下一个周期

    第二个输出 undefined,是因为立即执行执行 a 内部的方法,先 console.log(a),但此时的 a 还没赋值给左边的变量,所以只能是 undefined。然后 await b 就得等下一个周期执行了。

    第三个输出 end,自然不意外。

    接着输出 promise2,promise3,promise4,是因为 await b 等待他执行完了,才轮到 a 内部继续执行。

    输出 Promise { pending },脑筋转了以下才想通,事件都进入了循环了,a 肯定已经被赋值成了 Promise 对象。所以第二遍 console.log(a),自然就输出这个了。

    输出 after1 不奇怪。

    但是随后的 await a 是个什么奇怪的操作,想半天没搞懂为何最后不输出 after2,调试得知根本就执行不到 await a 以后的代码上,想不懂。

    更新:和不少朋友交流后,我得出了结论,await a 时,a 是必须等待 Promise 的状态从 pending 到 fullfilled 才会继续往下执行,可 a 的状态是一直得不到更改的,所以无法执行下面的逻辑。只要在 await a 上面加一行 resolve() 就能让后面的 after 2 得到输出。

    • 题4
    const promise = new Promise((resolve, reject) => {
      resolve('success1');
      reject('error');
      resolve('success2');
    });
    
    promise
      .then((res) => {
        console.log('then: ', res);
      })
      .catch((err) => {
        console.log('catch: ', err);
      });
    
    

    简单题,牢牢记住 Promise 对象的状态只能被转移一次,resolve('success1') 时状态转移到了 fullfilled 。后面 reject 就调用无效了,因为状态已经不是 pending。

    • 题5
    Promise.resolve()
      .then(() => {
        return new Error('error!!!')
      })
      .then((res) => {
        console.log('then: ', res)
      })
      .catch((err) => {
        console.log('catch: ', err)
      })
    

    没有抛出错误和异常,只是 return 了一个对象,哪怕他是 Error 对象,那自然也是正常走 then 的链式调用下去了,不会触发 catch。

    手写 Promise

    曾经实现过,挂在博客上JS 来实现一个 Promise

    简述:内部有个 state,doneList,failList,方法 resolve,reject,返回 fn(resolve, reject)。原型链有个 then,里面做的事情就是把函数参数推入 doneList 和 failList。然后 resolve 里执行的逻辑就是拿出 doneList 顺序执行下去,reject 执行 failList。

    怎么取消掉 Promise

    给你一个 promise,但是希望取消掉它。

    let promise = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(123);
      }, 1000);
    });
    

    axios 倒是有 abort 方法,但面试里你肯定不得用。

    promise 其实缺陷就是无法得知执行到了哪儿,也无法取消,只能被动的等 resolve 或者 reject 执行或者抛错。所以思路就是外部包裹一层 Promise,并对外提供 abort 方法,这个 abort 方法可以用来 reject 内部的 Promise 对象。

    function wrap(p) {
      let resol = null;
      let abort = null;
    
      let p1 = new Promise((resolve, reject) => {
        resol = resolve;
        abort = reject;
      });
    
      p1.abort = abort;
      p.then(resol, abort);
    
      return p1;
    }
    
    let newPromise = wrap(promise);
    
    newPromise.then(res => console.log)
    newPromise.abort()
    

    顺序输出

    不使用 async/await,给你若干个 promise 对象,你怎么保证它是顺序执行的?

    var makePromise = function(value, time) {
      return new Promise(function(resolve, reject){
        setTimeout(function() {
          resolve(value);
        }, time)
      })
    };
    
    function order(promises) {
    }
    
    order([
      makePromise('a', 3000),
      makePromise('b', 5000),
      makePromise('c', 2000),
    ]);
    

    思路是 then 里返回下一个

    function order(promises) {
      let dataArr = []
      let promise = Promise.resolve();
      for (let i = 0; i < promises.length; i++) {
        promise = promise.then((data) => {
          if (data) {
            dataArr.push(data);
            console.log(data);
          }
          return promises[i];
        });
      }
      return promise.then(data => {
        console.log(data);
      })
    }
    

    其实这么做有个问题,数组里的 promise 三项其实还是并发的,只不过输出的顺序的确符合期望,但是间隔的时间会比较奇怪,3s 后输出 a,再间隔 2s 连着输出了 b 和 c。

    要想让间隔时间也完全符合串行的话,那么还是在 then 里制造下一个 Promise 对象并返回吧。

    并发做异步请求,限制频率

    举个例子,有 8 张图片 url,你需要并发去获取它,并且任何时刻同时请求的数量不超过 3 个。也就是说第 4 张图片一定是等前面那一批有一个请求完毕了才能开始,以此类推。

    var urls = [
      'https://www.kkkk1000.com/images/getImgData/getImgDatadata.jpg',
      'https://www.kkkk1000.com/images/getImgData/gray.gif',
      'https://www.kkkk1000.com/images/getImgData/Particle.gif',
      'https://www.kkkk1000.com/images/getImgData/arithmetic.png',
      'https://www.kkkk1000.com/images/getImgData/arithmetic2.gif',
      'https://www.kkkk1000.com/images/getImgData/getImgDataError.jpg',
      'https://www.kkkk1000.com/images/getImgData/arithmetic.gif',
      'https://www.kkkk1000.com/images/wxQrCode2.png',
    ];
    
    function loadImg(url) {
      return new Promise((resolve, reject) => {
        const img = new Image();
        img.onload = () => {
          console.log('一张图片加载完成', url);
          resolve();
        };
        img.onerror = reject;
        img.src = url;
      });
    }
    
    function limitload(urls, limit) {
      
    }
    
    
    limitload(urls, 2);
    

    解法就是前 3 个放入一个外部的 promise 里同时进行,then 方法指定去请求后面的图片。

    function limitload(urls, limit) {
      let index = limit;
      function execNewPromise() {
        index += 1;
        if (index < urls.length) {
          return loadImg(urls[index]).then(() => execNewPromise());
        }
        
      }
      var promise = Promise.resolve();
      promise.then(() => {
        for (let i = 0; i < limit; i++) {
          loadImg(urls[i]).then(() => execNewPromise());  
        }
      });
      
    }
    

    总结

    单单能说事件循环机制本身还是不够,总有面试问题会非常恶心人,还是得多做几道备用,这个体验挺糟糕的,比让我手写 leetcode 算法题还难受。毕竟谁的代码会写成那样呢。

    要我手写 Promise 的公司,近两个月里出现了两家。第一次被问到时,虽然很久以前我写过并放入博客中,但真的上手写时还是很容易卡壳的,大家小心。

  • 相关阅读:
    github设置添加SSH
    pythonanywhere笔记
    Python3x 爬取妹子图
    python3.4 百度API接口
    简易博客开发(8)----django1.9 博客部署到pythonanywhere上
    Python3.4+Django1.9+Bootstrap3
    docker搭建私有仓库之harbor
    docker新手常见问题和知识点
    node之sinopia搭建本地npm仓库
    rancher-HA快速搭建
  • 原文地址:https://www.cnblogs.com/everlose/p/12950564.html
Copyright © 2020-2023  润新知