「面试手写系列」彻底搞懂Promise相关,保熟~

前言

Promise一直是面试中的热点,下面给大家介绍下Promise的相关方法

1. Promise.all

介绍

调用Promise.all时需要传入一个promise数组,我们称它为promiseArr,然后Promise.all会返回一个新的Promise,我们把它称为p

  • 如果promiseArr中有一个Promise失败,则走入pcatch回调中,并拿到对应的错误对象
  • 如果promiseArr里面全成功,则会走入pthen方法中,并能拿到之前传入的promiseArr对应的promise结果

原理

js 复制代码
function promiseAll(promiseArray) {
    return new Promise((resolve, reject) => {
        if(!Array.isArray(promiseArray)) {
            return reject(new Error('传入的参数必须是数组'))
        }
        const promiseNums = promiseArray.length
        const results = new Array(promiseNums)
        let count = 0
        promiseArray.forEach((promise, index) => {
            Promise.resolve(promise).then(res => {
                results[index] = res
                if(++count === promiseNums) {
                    resolve(results)
                }
            })
            .catch(reject)
        })
    })
}

注意点:

  • 需要默认返回一个promise
  • 存结果的results数组需要通过index和传入的promise数组一一对应

应用场景

  • 并行发送多个请求
  • 多个文件的并行读取
  • 并行执行多个任务

2. Promise.race

介绍

race的意思是赛跑,哪个Promise跑的快,也就是哪个Promise最先成功或失败,整个Promise.race也就对应的成功或失败。

原理

js 复制代码
function PromiseRace(promiseArray) {
    return new Promise((resolve, reject) => {
        if(!Array.isArray(promiseArray)) {
            return reject(new Error('传入的参数必须是数组'))
        }
        promiseArray.forEach((promise, index) => {
            Promise.resolve(promise).then(res => {
                resolve(res)
            })
            .catch(reject)
        })
    })
}

应用场景

  • 尽快拿到请求结果 :比如多个接口都可以拿到你想请求的数据,你就可以用Promise.race,如果拿到了一个请求的响应结果,你就可以直接渲染页面了,这样能加快我们的页面响应速度。
  • 多个资源加载 :你可以把手动去加载多个资源,然后用Promise.race来等待最快加载的资源后采取行动,以提高加载性能。
  • 竞态条件处理:同时尝试多个可能的解决方案,并采取第一个可用的解决方案。

3. Promise.prototype.finally

介绍

finallythencatch一样,都是Promise原型上的方法,与thenreject一同,不管Promise成功还是失败,最终都会执行finally方法。

原理

js 复制代码
Promise.prototype.finally = function (cb) {
  return this.then(
    (y) => {
      return Promise.resolve(cb()).then(() => y);
    },
    (r) => {
      return Promise.resolve(cb()).then(() => {
        throw r;
      }); 
    }
  );
};

应用场景

  1. 清理资源或状态 :比如你需要在最Promise执行结束之后释放一些资源,比如打开的文件数据库连接网络连接等,或者释放状态,比如页面的loading
  2. 执行收尾操作 :比如无论Promise成功或者失败,你都要执行一些收尾工作,比如记录日志、发送统计信息或触发一些事件等。
  3. 统一处理 :在thencatch的时候都需要进行的处理,这时候你就不需要写两次重复的代码了,直接放在finally当中。

4. Promise.allSettled

介绍

  1. Promise.allSettled()方法接收一组Promise作为参数,返回一个新的Promise实例
  2. 只有等到所有的这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束
  3. 返回新的Promise实例,一旦结束,状态总是fulfilled,不会变成rejected
  4. 新的promise实例给监听函数传递一个数组results。该数组的每个成员都是一个对象,对应传入Promise.allSettled的Promise实例。每个对象都有status属性,对应着fulfilled和rejected。fulfilled时,对象有value属性,rejected时有reason属性,对应两种状态的返回值。
  5. 当我们不需要关心异步操作的结果,只会关心这些操作有没有结束的时候,这时候Promise.allSettled就派上用场了。

原理

js 复制代码
const formatSettledResult = (isSuccess, value) => {
  return isSuccess ? ({ status: 'fulfilled', value }) : ({ status: 'rejected', reason: value })
}

Promise.all_settled = function (promises) {
  if (!Array.isArray(promises)) {
    throw new Error('传入的参数必须是数组y')
  }
  let num = 0, len = promises.length, results = Array(len)

  return new Promise((resolve, reject) => {
    promises.forEach((promise, index) => {
      Promise.resolve(promise).then((value) => {
        results[index] = formatSettledResult(true, value)
        if (++num === len) {
          resolve(results)
        }
      })
      .catch((reason) => {
        results[index] = formatSettledResult(false, reason)
        if (++num === len) {
          resolve(results)
        }
      })
    })
  })
}

应用场景

  1. 并行请求:当我们需要并行发送多个请求,并在所有请求完成后获得每个请求的结果和状态信息
  2. 批量处理:在等待所有的请求完成后,对各个请求的状态分别进行处理。
  3. 处理多个资源加载:可以将资源的加载封装为promise,并同时加载多个资源,并在加载完后根据promise的状态对每个资源进行对应的处理。

5. Promise.cache

介绍

利用promise实例化立即执行的特性可以做请求的缓存。

原理

js 复制代码
const cacheMap = new Map()
function enableCache(target, name, descriptor) {
    const val = descriptor.value
    descriptor.value = async function (...args) {
        const cacheKey = name + JSON.stringify(args)
        if (!cacheMap.get(cacheKey)) {
            const cacheValue = Promise.resolve(val.apply(this, args)).catch(() => {
                cacheMap.set(cacheKey, null)
            })
            cacheMap.set(cacheKey, cacheValue)
        }
        return cacheMap.get(cacheKey)
    }
    return descriptor
}

class PromiseClass {
    // 装饰器
    // @enableCache
    static async getInfo() {

    }
}
PromiseClass.getInfo()
PromiseClass.getInfo()
PromiseClass.getInfo()

这里我先定义一个Map作为缓存对象,然后用方法名,也就是上面的getInfo + 参数序列化后的值作为缓存key值,然后就可以实现请求的缓存了,当然我这里缓存key设置的比较简单,实际业务场景肯定会更为严谨一些,然后业务中如果用了缓存的话,需要考虑缓存失效的问题,过快失效和过久不失效都可能会让程序出现bug,需要注意一下。

6. Promise.limit

介绍

Promise.limit可以通过promise实现并发控制

原理

js 复制代码
function limitLoad(promiseArray, limit) {
    const results = [];
    const promises = promiseArray.slice(0, limit).map((promise, index) => {
        return promise.then((value) => {
            results[index] =  value;
            return index;
        })
    })
    let p = Promise.race(promises)
    for(let i = limit; i < promiseArray.length; i++) {
        p = p.then((index) => {
            promises[index] = promiseArray[i].then((value) => {
                results[i] = value;
                return value
            })
            return Promise.race(promises)
        })
    }
    return p.then(() => results);
}

首先,声明一个results数组存储promise结果,然后先取出limit个数的promise,通过Promise.race可以拿到最快执行完的那一个,我们前面会把每个promise对应在promises数组的位置index往下传递,然后通过循环串成一个promise链,在Promise.race的then方法中,通过前面的index找到那个最快执行完的promise所在的位置,将其替换,最终promise链执行完将存储promise结果results数组返回就行了。

应用场景

  1. 并发请求控制 :有时候为避免服务器性能问题,可以使用Promise.limit进行并发控制,以提高请求的响应速度
  2. 批量处理限制 :当对大量数据进行处理时,有时候一次性处理会导致服务器内存溢出,这时可以采用Promise.limit控制同时处理的数据数量,以提高资源的处理效率。
  3. 队列调度 :在任务调度和队列管理中,可以使用Promise.limit将所有任务放在固定大小的任务池中,并限制同时执行的任务数量,这样可以确保任务按照限制的并发度进行顺序执行,避免资源竞争和过度负载

小结

以上给大家介绍了6个Promise相关方法以及应用场景,马上快金九银十了,希望大家都能熟练掌握好promise相关,出去面试offer拿到手软

如果还有兴趣想掌握手写Promise"吊打面试官",可以移步我之前写的文章,手写Promise

相关推荐
Fan_web10 分钟前
jQuery——事件委托
开发语言·前端·javascript·css·jquery
安冬的码畜日常11 分钟前
【CSS in Depth 2 精译_044】第七章 响应式设计概述
前端·css·css3·html5·响应式设计·响应式
莹雨潇潇1 小时前
Docker 快速入门(Ubuntu版)
java·前端·docker·容器
Jiaberrr1 小时前
Element UI教程:如何将Radio单选框的圆框改为方框
前端·javascript·vue.js·ui·elementui
Tiffany_Ho2 小时前
【TypeScript】知识点梳理(三)
前端·typescript
安冬的码畜日常3 小时前
【D3.js in Action 3 精译_029】3.5 给 D3 条形图加注图表标签(上)
开发语言·前端·javascript·信息可视化·数据可视化·d3.js
太阳花ˉ3 小时前
html+css+js实现step进度条效果
javascript·css·html
小白学习日记4 小时前
【复习】HTML常用标签<table>
前端·html
john_hjy4 小时前
11. 异步编程
运维·服务器·javascript