Promise总结

104 阅读7分钟

什么是 promise

  1. promise 是 JS 中进行异步变成的新的解决方案
  2. 具体表达
  • 从语法功能上来说:promise 是一个构造函数
  • 从功能上来说:promise 对象用来封装一个异步操作并可以获取其结果
  1. promise 的状态改变
  • pending 变成 resolved(pending 待验证->已解决)
  • pending 变成 rejected(pending 待定->被拒绝) 说明:只有这两种状态改变,并且一个 promise 对象只能改变一次 无论变成成功还是失败,都会有一个结果数据 成功的结果数据一般称为 value,失败的结果数据一般称为 reason

为什么要使用 promise

  1. 指定回调函数的方式更加灵活 旧的: 必须在启动异步任务前指定 promise: 启动异步任务=> 返回 promise 对象 =>给 promise 对象绑定回调函数
  2. 支持链式调用,可以解决回调地狱问题 promise 链式调用,终极解决方案是 async/await

promise和async/await的区别

  1. promise是ES6,async/await是ES7
  2. 相对于promise来讲,写法更加优雅
  3. reject状态

a. promise错误可以通过catch来捕捉,建议尾部捕获错误 b. async/await既可以用,.then又可以用try-catch捕捉

手写Promise

需要注意的点:

  1. promise实例的 **then()** 方法最多接受两个参数:用于 Promise 兑现和拒绝情况的回调函数。onReject一个在此 Promise 对象被拒绝时异步执行的函数。它的返回值将成为 catch() 返回的 Promise 对象的兑现值。如果 onRejected 不是一个函数,则内部会被替换为一个_抛出器_函数((x) => { throw x; }),它会抛出它收到的拒绝原因。重点就是onRejected只有返回了内容,才会被catch捕捉到,没有返回内容就默认上一次promise是resolve掉了,后面的then执行的是上次onFulfilled的结果。
  2. 当resolve或者reject在定时器里面,那么定时器结束后再执行then

链式调用关键点

  • 1、then方法本身会返回一个新的Promise对象
  • 2、如果返回值是promise对象,返回值为成功,新promise就是成功
  • 3、如果返回值是promise对象,返回值为失败,新promise就是失败
  • 4、如果返回值非promise对象,新promise对象就是成功,值为此返回值
// 链式调用 输出200
const p3 = new Promise((resolve,reject)=>{
  resolve(100)
}).then(res=>res*2,err=>console.log(err))
    .then(res=>console.log(res),err=>console.log(err))

// 链式调用 输出300
const p4 = new Promise((resolve,reject)=>{
  resolve(100)
  // 返回值受新的promise影响,新的promise成功则成功 新的promise失败则失败
}).then(res => new Promise((resolve, reject) => resolve(3 * res)), err => console.log(err))
    .then(res => console.log(res), err => console.log(err))

手写Promise代码+例子演示

class MyPromise {
  // 构造方法
  constructor(executor){
    this.initValue()
    this.initBind()
    try{
      executor(this.resolve,this.reject)
    }catch(e){
      // Promise中有throw的话,就相当于执行了reject
      this.reject(e)
    }
  }
  initBind(){
    this.resolve = this.resolve.bind(this)
    this.reject = this.reject.bind(this)
    this.onFulfilledCallbacks = [] // 保存成功的回调
    this.onRejectedCallbacks = [] // 保存失败的回调
  }
  initValue(){
    this.PromiseResult = null
    this.PromiseState = 'pending'
  }
  resolve(value){
    if(this.PromiseState !== 'pending') return
    this.PromiseState = 'fulfilled'
    this.PromiseResult = value
    while(this.onFulfilledCallbacks.length){
      this.onFulfilledCallbacks.shift()(this.PromiseResult)
    }
  }
  reject(reason){
    if(this.PromiseState !== 'pending') return
    this.PromiseState = 'rejected'
    this.PromiseResult = reason
    // 执行保存的失败回调
    while(this.onRejectedCallbacks.length){
      this.onRejectedCallbacks.shift()(this.PromiseResult)
    }
  }
  then(onFulfilled,onRejected){
    // 参数校验确保是函数
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v
    onRejected = typeof onRejected === 'function' ? onRejected : (reason)=>{throw reason}
    const thenPromise = new MyPromise((resolve,reject)=>{
      const resolvePromise = cb => {
        // then是微任务 为了模拟这个特性,加上setTimeout
        setTimeout(() => {
          try{
            const x = cb(this.PromiseResult)
            if(x===thenPromise){
              // 不能返回自身
              throw new Error('不能返回自身')
            }
            if(x instanceof MyPromise){
              x.then(resolve,reject)
            }else{
              resolve(x)
            }
          }catch(err){
            reject(err)
            throw new Error(err)
          }
        })
        
      }
      if(this.PromiseState === 'fulfilled'){
        resolvePromise(onFulfilled)
      }else if(this.PromiseState === 'rejected'){
        resolvePromise(onRejected)
      }else if(this.PromiseState === 'pending'){
        // executor是异步的,不能保证延迟执行then,但是可以保证延迟执行then的回调
        // 也就是说把then的回调函数缓存起来 等执行了resolve或者reject再去执行这个缓存的函数
        this.onFulfilledCallbacks.push(resolvePromise.bind(this,onFulfilled))
        this.onRejectedCallbacks.push(resolvePromise.bind(this,onRejected))
      }
    })
    // 返回这个包装的promise
    return thenPromise
  }
}
// 输出 ”成功“
const test = new MyPromise((resolve, reject) => {
  resolve('成功')
}).then(res => console.log(res), err => console.log(err))

// 只能改变一次状态
const test1 = new MyPromise((resolve, reject) => {
  // 只以第一次为准
  resolve('成功')
  reject('失败')
})
console.log(test1) // MyPromise { PromiseState: 'fulfilled', PromiseResult: '成功' }

// 定时器输出结果
const test2 = new MyPromise((resolve, reject) => {
  setTimeout(() => {
      resolve('成功') // 1秒后输出 成功
      // reject('失败') // 1秒后输出 失败
  }, 1000)
}).then(res => console.log(res), err => console.log(err))

// resolve reject 输出
const test3 = new Promise((resolve, reject) => {
  resolve(100) // 输出 状态:成功 值: 200
  // reject(100) // 输出 状态:成功 值:300
}).then(res => 2 * res, err => 3 * err)
  .then(res => console.log('成功', res), err => console.log('失败', err))

// 测试链式调用then
const test4 = new Promise((resolve, reject) => {
    // resolve(100) // 输出 状态:失败 值:200
    reject(100) // 输出 状态:成功 值:300
  }).then(res => new Promise((resolve, reject) => reject(2 * res)), err => new Promise((resolve, reject) => resolve(3 * err)))
    .then(res => console.log('成功', res), err => console.log('失败', err))

// promise.then微任务测试
const test5 = new MyPromise((resolve, reject) => {
  resolve(1)
}).then(res => console.log(res), err => console.log(err))

console.log(2)



promise 的 api

  • Promise.resolve - 制造一个成功或者失败

在一个promise.resolve()中再嵌套一层promise,并返回错误结果,那么最终结果就是失败

function test(){
  return Promise.resolve(new Promise((resolve,reject)=>reject('err')))
}
test().then(e=>console.log(e))
  • Promise.reject - 制造一个失败

Promise.all()

并行执行所有 promise,等待全部成功,只要有一个失败,就不会继续执行其他的 promise

static all(promises) {
  const result = []
  let count = 0
  return new MyPromise((resolve,reject)=>{
    const addData = (index,value)=>{
      result[index] = data
      count++
      if(count === promises.length) resolve(result)
    }
    promises.forEach((promise,index)=>{
      if(promise instanceof MyPromise){
        promise.then((res)=>{
          addData(index,res)
        },err => reject(err))
      }else{
        addData(index,promise)
      }
    })
  })
}

Promise.allSettled

可以打印出所有prmomise的结果和状态

static allSettled(promises){
  return new MyPromise((resolve, reject) => {
    const res = []
    let count = 0
    const addData = (status,value,i)=>{
      res[i] = {
        status,
        value
      }
      count++
      if(count === promises.length){
        resolve(res)
      }
    }
    promises.forEach((promise,i)=>{
      if(promise instanceof MyPromise){
        promise.then((res)=>{
          addData('fulfilled',res,i)
        },err => addData('rejected',err,i))
      }else{
        addData('fulfilled',promise,i)
      }
    })
  })
}

Promise.race

哪个请求先完成就输出哪个

static race(promises) {
  return new MyPromise((resolve, reject) => {
    promises.forEach((promise)=>{
      if(promise instanceof MyPromise){
        promise.then(res=>{
          resolve(res)
        },err=>reject(err))
      }else{
        resolve(promise)
      }
    })
  })
}

Promise.any

any与all相反

  • 接收一个Promise数组,数组中如有非Promise项,则此项当做成功
  • 如果有一个Promise成功,则返回这个成功结果
  • 如果所有Promise都失败,则报错
static any(promises) {
  return new Promise((resolve,reject)=>{
    let count = 0
    promises.forEach((promise,i)=>{
      if(promise instanceof Promise){
        promise.then(val=>resolve(val),
        err=>{
          count++
          if(count === promises.length){
            reject(new AggregateError('All promises were rejected'))
          }
    })
      }else{
        resolve(promise)
      }
    
    })
  })
}

Promise题目总结

实现一个promise.retry

Promise.retry接口 1、参数1:返回Promise的函数 2、参数2:重试次数 3、返回promise

Promise.retry = function(cb,times){
  return new Promise((resolve,reject)=>{
    function next(){
      cb.then((res)=>{
        resolve(times)
      }).catch((e)=>{
        if(times){
          next()
          times--
        }else{
          reject(e)
        }
      })
    }
    next()
  })
}

promise多请求并发控制

import axios from "axios"

const requestQueue = (concurrency = 6)=>{
      const queue = [];
      let current = 0;
    const dequeue = () => {
      while(current < concurrency && queue.length){
        current ++;
        const temp = queue.shift()
        temp.then((res)=>{}).catch((e)=>{})
        .finally(()=>{
          current--;
          dequeue()
        })
    }
  }
  return (resF)=>{
    queue.push(resF)
    dequeue()
  }
}

const enqueue = requestQueue()
for(let i = 0; i<req.length;i++){
  enqueue(()=>axios.get('/api/test'+i))
}

如下的promise.all会输出什么

  • 首先同步输出 start 0 start 1000 start 2000 start 3000 start 4000,因为primise之外的可以同步执行
  • 其次间隔指定时间输出 end 0 end 1000 end 2000 end 3000 end 4000
  • 最后输出一个数组 [0, 1000, 2000, 3000, 4000]
// createTask有两层函数
function createTask(ms) {
  return () => {
    // primise之外的可以同步执行
    console.log("start", ms);
    // promise之内的是异步执行
    return new Promise(r =>
      setTimeout(() => {
        console.log("end", ms);
        r(ms);
      }, ms)
    );
  };
}
// 此处相当于调用createTask外部的函数
const tasks = Array(5)
  .fill(null)
  .map((_, i) => createTask(i * 1000));
// 此处相当于调用createTask外=内部的函数
// promise.all是需要等所有的promise执行完毕才能进行then
Promise.all(tasks.map(task => task())).then(res => {
  console.log(res);
});

实现一个limitTask函数,同时只能有2个任务进行

核心就是递归,promise.all,promise.then

limitRunTask(tasks, 2).then(console.log);

function limitRunTask(alltasks, limitNumber) {
  let loop = 0
  let resArr = []
  function run(tasks) {
    if (!tasks.length) return Promise.resolve(resArr)
    return Promise.all(tasks.map(() => tasks()))
      .then((res) => {
        resArr.push(...res)
        console.log(loop, '任务结束')
        loop++
        return run(
          alltasks.slice(loop * limitNumber, loop * limitNumber + limitNumber)
        )
      })
  }
  run(alltasks.slice(loop * limitNumber, loop * limitNumber + limitNumber))
}

实现一个 Queue类,按照指定间隔执行函数,当start函数被调用的时候才开始

实现一个Queue类,要求包含两个函数 task函数:新增一个任务。包含两个参数,等待时间和回调函数 ; start函数:执行任务队列。将所有任务按队列顺序执行,执行完一个任务才能执行下一个任务 ps:下面代码,实现添加3个任务,然后执行3个任务。隔1秒,打印1;再隔2秒,打印2;再隔1秒,打印3 其实也是一个递归,不过限制条件变成1了,因为要一个一个的去执行

class Queue {
  constructor(){
    this.allTasks = [];
    this.limitNumber = 1;
    this.loop = 0;
  }
  task(wait,cb){
    this.allTasks.push({wait,cb});
    // 因为此处是链式调用 所以要返回this
    return this
  }
  start(){
    // 启动任务
    return this.run(
      this.allTasks.slice(
      this.loop*this.limitNumber, this.loop*this.limitNumber + this.limitNumber
      )
    )
  }
  run(tasks){
    const detail = tasks[0];
    if(!detail){
      this.loop = 0;
      return Promise.resolve()
    }
    return new Promise((resolve,reject)=>{
      setTimeout(()=>{
        detail.cb()
        this.loop++;
        resolve()
      },detail.wait)
    }).then(()=>{
      this.run(
        this.allTasks.slice(
        this.loop*this.limitNumber, this.loop*this.limitNumber + this.limitNumber
        )
      )
    })
  }

}
new Queue()
.task(1000,()=>{console.log(1)})
.task(2000,()=>{console.log(2)})
.task(3000,()=>{console.log(3)})
.start()

参考文章