Глубокое понимание принципа Promise и полнофункциональная реализация

JavaScript

открытие

PromiseКак интерфейсное асинхронное решение, можно сказать, что оно популярно во всей сети, почти все асинхронные сценарии и даже фреймворки будут присутствовать, напримерVueпакетная обработка и т.д. Сегодня мы будем следитьPromise A+спецификации для полной реализацииPromiseПолная функция, нечего сказать по коду.

Обещание реализации

статус определение статуса

PromiseНастройка представляет собой необратимый конечный автомат, содержащий:

const PENDDING = "PENDDING"; // 初始化pendding状态
const RESOLVED = "RESOLVED"; // 正确完成resolve状态
const REJECTED = "REJECTED"; // 错误完成reject状态

MyPromise

СоздайтеMyPromiseЗначения и состояния, соответствующие функциям класса и инициализациям

class MyPromise {
  constructor(executor) {
    // 初始化状态status
    // 返回值value
    // 错误原因reason
    this.status = PENDDING;
    this.value = undefined;
    this.reason = undefined;

    // 返回值回调队列和错误回调队列
    this.resolves = [];
    this.rejects = [];

    // 声明resolve函数
    const resolve = (value) => {
      if (this.status === PENDDING) {
        this.status = RESOLVED; // 变更状态为完成状态
        this.value = value; // 赋值

        // 执行resolves队列
        while (this.resolves.length) {
          const callback = this.resolves.shift();
          callback(value);
        }
      }
    };

    // 声明reject函数
    const reject = (reason) => {
      if (this.statue === PENDDING) {
        this.status = REJECTED; // 变更状态为拒绝状态
        this.reason = reason; // 赋值

        // 执行rejects队列
        while (this.rejects.length) {
          const callback = this.rejects.shift();
          callback(reason);
        }
      }
    };
    
    try{
    	executor(resolve,reject)
    }catch(e){
    	reject(e)
    }
  }
}

MyPromise.then

синхронный и асинхронный

class MyPromise {
  // ...
  then(resolve, reject) {
    // 完成状态,推入完成队列
    if (this.status === RESOLVED) {
      resolve(this.value);
    }

    // 拒绝状态,推入拒绝队列
    if (this.status === REJECTED) {
      reject(this.reason);
    }

    // 异步情况
    if (this.status === PENDDING) {
      this.resolves.push(resolve);
      this.rejects.push(reject);
    }
  }
  // ...
}

// 测试同步任务

const promise = new MyPromise((resolve, reject) => {
    resolve('promise sync')
})

promise.then(res => {
    console.log(res)
})

// 打印结果
// promise sync

// 测试异步任务
const promise = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve('promise async)
    }, 500)
})

promise.then(res => {
    console.log(res)
})

// 打印结果
// promise async

Сценарий спецификации Promise A+

В соответствии со спецификацией Promise A+ метод then возвращает обещание, что позволяет выполнять нисходящую цепочку вызовов. В то же время, по возвращаемому значению предыдущего метода then, его можно прозрачно передать следующему методу then.

// Promise A+
const promise = new Promise((resolve, reject) => {
  resolve("first");
});

// 第一种场景:返回常规值
promise
  .then((res) => {
    console.log(res);

    return "second";
  })
  .then((res) => {
    console.log(res);
  });

// 打印结果
// first
// second

// 第二种场景:返回promise
promise
  .then((res) => {
    console.log(res);
    return new Promise((resolve) => {
      resolve("promise");
    });
  })
  .then((res) => {
    console.log(res);
  });

// 打印结果
// first
// promise

// 第三种场景:值穿透
promise
  .then((res) => {
    console.log(res);
    return res;
  })
  .then()
  .then((res) => {
    console.log(res);
  });

// 打印结果
// first
// first

реализовать тогда

Согласно приведенному выше определению спецификации, давайте преобразуемthenметод:

class MyPromise {
  // ...
  then(resolve, reject) {
    // 判断resolve和reject未传入的情况,解决空值透传问题
    // then()情况
    typeof resolve !== 'function' ? resolve = value => value : resolve
    typeof reject !== 'function' ? reject = reason => throw new Error(reason instanceof Error ? reason.message : reason )

    //根据规范,then会返回一个全新的promise
    return new MyPromise((resolveFn, rejectFn) => {
        // 重写传入的resolve方法
        // 判断返回值
        const fulfilished = value => {
            try{
                // 接收返回值
                const res = resolve(value)

                // 判断返回值类型:promise或普通类型
                // 如果是promise,则往下执行一次then
                // 如果是普通类型,则直接执行resolveFn,保证value是最新值
                res instanceof MyPromise ? res.then(resolveFn,rejectFn) : resolveFn(res)
            }catch(e) {
                rejectFn(e)
            }
        }

        // 重写传入的reject方法
        // 判断返回值
        const rejected = reason => {
            try{
        // 接收返回值
                const res = reject(reason)

                // 判断返回值类型:promise或普通类型
                // 如果是promise,则往下执行一次then
                // 如果是普通类型,则直接执行rejectFn,保证value是最新值
                res instanceof MyPromise ? res.then(resolveFn,rejectFn) : rejectFn(res)
            }catch(e){
                rejectFn(e instanceof Error ? e.message: e)
            }

        }

        // 判断同步异步任务
        // 执行相对应的方法
        // 这里用switch方法改进
        switch(this.status) {
            case RESOLVED:
                fulfilished(this.value)
            break;

            case REJECTED:
                rejected(this.reason)
            break;

            case PENDDING:
                this.resolves.push(fulfilished)
                this.rejects.push(rejected)
              break;
        }
    })

  }
  // ...
}

// 测试
const promise = new MyPromise((resolve, reject) => {
    resolve('first')
})

promise.then(res => {
    console.log(res)
    return new MyPromise((resolve, reject) => {
        resolve('promise second')
    })
}).then().then(res => {
    console.log(res)
    return 'third'
}).then(res => {
    console.log(res)
})

// 打印结果
// first
// promise second
// third

резюме

испытание прошло успешно,promiseДаже если модификация соответствует спецификации. Трудность в том, чтоthenЕсли возвращаемое значение внутренней функции равноpromise, то позволим ему выполнить регистрацию один разthen,ПозволятьpromiseЗатем спускайтесь.

MyPromise.catch

catchМетод относительно прост, поместите отклоненное значение вrejectметод может быть выполнен.

Сценарий спецификации Promise A+

// Promise A+
const promise = new Promise((resolve, reject) => {
  reject("promise reject");
});

promise.catch((e) => {
  console.log(e);
});

// 打印结果
// promise reject

реализовать улов

class MyPromise {
  // ...
  catch(errorFn) {
    // 这里只需注册执行下then,传入callback就能实现
    this.then(null, errorFn);
  }
  // ...
}

// 测试
const promise = new MyPromise((resolve, reject) => {
  reject("my promise reject");
});

promise.catch((e) => {
  console.log(e);
});

// 打印结果
// my promise reject

резюме

catchМетод заключается в выполнении обратного вызова для полученияreject, поэтому просто выполнитеthenи пройти вcallbackЭто реализовано, относительно легко понять.

MyPromise.all

В бизнес-сценариях мы часто сталкиваемся с более чем однимpromie, поэтому вам нужно объединить несколько исполнений одновременноpromise, равномерно вернуть результат,Promise.allКак раз для решения этой проблемы.

Сценарий спецификации Promise A+

  // Promise A+
  // 创建三个promise
  const promise1 = Promise.resolve(1)
  const promise2 = Promise.resolve(2)
  const promise3 = Promise.resolve(3)

  Promise.all([promise1,promise12,promise3]).then(res => {
    console.log(res)
  })

  // 打印结果
  // [1,2,3]

  // 添加一个reject
  const promise4 = Promise.resolve(1)
  const promise5 = Promise.reject('reject')
  const promise6 = Promise.resolve(3)

  Promise.all([promise4, promise5,promise6]).then(res => {
    console.log(res, 'resolve')
  }).catch(e => {
    console.log(e)
  })

  // 打印结果
  // reject

Согласно спецификации Promise A+, Promise.all может выполнять несколько промисов одновременно и возвращает возвращаемое значение массива после возврата всех методов промисов. Когда одно из обещаний отклоняется, возвращается результат отклонения.

Реализовать обещание.все

Давайте реализуем это:

  class MyPromise {
    // ...
    // all是静态方法
    static all(promises) {
      // 已然是返回一个promise
      return new MyPromise((resolve, reject) => {
        // 创建一个收集返回值的数组
        const result = []

        // 执行
        deepPromise(promises[0], 0 , result)

        // 返回结果
        resolve(result)

        // 这里我们用递归来实现
        // @param {MyPromise} promise 每一个promise方法
        // @param {number} index 索引
        // @param {string[]} result 收集返回结果的数组
        function deepPromise(promise, index, result) {
          // 边界判断
          // 所有执行完之后返回收集数组
          if(index > promises.length - 1) {
            return result
          }

          if(typeof promise.then === 'function') {
            // 如果是promise
            promise.then(res => {
              index++
              result.push(res)
              deepPromise(promises[index], index, result)
            }).catch(e => {
              // reject直接返回
              reject(e instanceof Error ? e.message : e)
            })
          }else {
            // 如果是普通值
            // 这里我们只做简单判断,非promise则直接当返回值处理
            index++
            result.push(promise)
            deepPromise(promises[index], index, res)
          }
        }
      })

    }
    // ...
  }

  // 测试
  // 创建三个MyPromise
  const promise1 = MyPromise.resolve(1)
  const promise2 = MyPromise.resolve(2)
  const promise3 = MyPromise.resolve(3)

  MyPromise.all([promise1,promise12,promise3]).then(res => {
    console.log(res)
  })

  // 打印结果
  // [1,2,3]

  // 添加一个reject
  const promise4 = MyPromise.resolve(1)
  const promise5 = MyPromise.reject('reject')
  const promise6 = MyPromise.resolve(3)

  MyPromise.all([promise4, promise5,promise6]).then(res => {
    console.log(res, 'resolve')
  }).catch(e => {
    console.log(e)
  })

  // 打印结果
  // reject

резюме

Promise.allВ качестве функции пакетной обработки мы можем использовать несколько обработок одновременно.promise, что упрощает недостаток выполнения по одному. Основная логика также относительно проста, наиболее важным моментом является выполнениеpromiseперейти к следующемуpromiseПосле обработки этой логики он в основном завершенPromise.allполностью функциональна.

MyPromise.resolve

статический методresolveРеализация относительно проста, возвращаяpromise, вы можете передать соответствующие параметры.

Реализовать MyPromise.resolve

  class MyPromise {
    // ...
    static resolve(value) {
      return new MyPromise((resolveFn, rejectFn) => {
        resolveFn(value)
      })
    }
    // ...
  }

  // 测试
  MyPromise.resolve('static resolve').then(res => {
    console.log(res)
  })

  // 打印结果
  // static resolve

MyPromise.reject

статический методrejectреализация иresolveаналогично, возвращаетpromise, вы можете передать соответствующие параметры.

  class MyPromise {
    // ...
    static reject(reason) {
      return new MyPromise((resolveFn, rejectFn) => {
        rejectFn(reason)
      })
    }
    // ...
  }

  // 测试
  MyPromise.reject('static reject').catch(e => {
    console.log(res)
  })

  // 打印结果
  // static reject

Что такое allSetted?

Последнее обновление официального сайта ECMAPromiseновый статический методPromise.allSettled, то что это за метод? А вообще он тоже пакетная обработкаPromiseфункцию, но у нас уже естьPromise.allЗачем тебе нужноallSettled. Чтобы разгадать это, мы должны оглянуться назадPromise.all. токPromise.allМы сказали, что еслиPromiseЕсть один в очередиreject, то он просто возвращаетсяreject,такPromise.allНе обязательно все результаты будут возвращены, очевидноPromise.allSettledможет решить эту проблему.

Сценарий тестирования Promise A+

   // Promise A+
  // 创建三个promise
  const promise1 = Promise.resolve(1)
  const promise2 = Promise.resolve(2)
  const promise3 = Promise.resolve(3)

  Promise.allSettled([promise1,promise12,promise3]).then(res => {
    console.log(res)
  })

  // 打印结果
  /*
  [
    {status: 'fulfilished', value: 1},
    {status: 'fulfilished', value: 2},
    {status: 'fulfilished', value: 3}
  ]
  */

  // 添加一个reject
  const promise4 = Promise.resolve(1)
  const promise5 = Promise.reject('reject')
  const promise6 = Promise.resolve(3)

  Promise.allSettled([promise4, promise5,promise6]).then(res => {
    console.log(res, 'resolve')
  }).catch(e => {
    console.log(e)
  })

  // 打印结果
   /*
  [
    {status: 'fulfilished', value: 1},
    {status: 'rejected', value: 'reject'},
    {status: 'fulfilished', value: 3}
  ]
  */

можно увидетьallSettledСамое большое отличие от всех в том, что,allSettledне важно какresolve,ещеrejectможет вернуть результирующий массив полностью, но каждый элемент массива выводится в виде объекта,statusописать состояние,valueПолучите возвращаемое значение.

Реализовать MyPromise.allSettled

allSettledобщая логикаallто же самое, но возвращаемое значение обрабатывается немного по-другому

  class MyPromise {
// ...
  static allSettled(promises) {
      // 已然是返回一个promise
      return new MyPromise((resolve, reject) => {
        // 创建一个收集返回值的数组
        const result = []

        // 执行
        deepPromise(promises[0], 0 , result)

        // 返回结果
        resolve(result)

        // 这里我们用递归来实现
        // @param {MyPromise} promise 每一个promise方法
        // @param {number} index 索引
        // @param {string[]} result 收集返回结果的数组
        function deepPromise(promise, index, result) {
          // 边界判断
          // 所有执行完之后返回收集数组
          if(index > promises.length - 1) {
            return result
          }

          if(typeof promise.then === 'function') {
            // 如果是promise
            promise.then(res => {
              index++
              result.push({status: 'fulfilished', value: res}) // 这里推入的是对象
              deepPromise(promises[index], index, result)
            }).catch(e => {
              // reject直接返回
              index ++
              result.push({status: 'rejected', value: res}) // 这里推入的是对象
              deepPromise(promises[index], index, result)
            })
          }else {
            // 如果是普通值
            // 这里我们只做简单判断,非promise则直接当返回值处理
            index++
            result.push({status: 'fulfilished', value: res}) // 这里推入的是对象
            deepPromise(promises[index], index, res)
          }
        }
      })

    }
  // ...
  }

  // 测试
  // 创建三个promise
  const promise1 = MyPromise.resolve(1)
  const promise2 = MyPromise.resolve(2)
  const promise3 = MyPromise.resolve(3)

  MyPromise.allSettled([promise1,promise12,promise3]).then(res => {
    console.log(res)
  })

  // 打印结果
  /*
  [
    {status: 'fulfilished', value: 1},
    {status: 'fulfilished', value: 2},
    {status: 'fulfilished', value: 3}
  ]
  */

  // 添加一个reject
  const promise4 = MyPromise.resolve(1)
  const promise5 = MyPromise.reject('reject')
  const promise6 = MyPromise.resolve(3)

  Promise.allSettled([promise4, promise5,promise6]).then(res => {
    console.log(res, 'resolve')
  }).catch(e => {
    console.log(e)
  })

  // 打印结果
   /*
  [
    {status: 'fulfilished', value: 1},
    {status: 'rejected', value: 'reject'},
    {status: 'fulfilished', value: 3}
  ]
  */

полный код

class MyPromise {
  constructor(executor) {
    // 初始化状态status
    // 返回值value
    // 错误原因reason
    this.statue = PENDDING;
    this.value = undefined;
    this.reason = undefined;

    // 返回值回调队列和错误回调队列
    this.resolves = [];
    this.rejects = [];

    // 声明resolve函数
    const resolve = (value) => {
      if (this.status === PENDDING) {
        this.status = RESOLVED; // 变更状态为完成状态
        this.value = value; // 赋值

        // 执行resolves队列
        while (this.resolves.length) {
          const callback = this.resolves.shift();
          callback(value);
        }
      }
    };

    // 声明reject函数
    const reject = (reason) => {
      if (this.statue === PENDDING) {
        this.status = REJECTED; // 变更状态为拒绝状态
        this.reason = reason; // 赋值

        // 执行rejects队列
        while (this.rejects.length) {
          const callback = this.rejects.shift();
          callback(reason);
        }
      }
    };
    
    try{
    	executor(resolve,reject)
    }catch(e){
    	reject(e)
    }
  }

  // then
  then(resolve, reject) {
    // 判断resolve和reject未传入的情况,解决空值透传问题
    // then()情况
    typeof resolve !== 'function' ? resolve = value => value : resolve
    typeof reject !== 'function' ? reject = reason => throw new Error(reason instanceof Error ? reason.message : reason): reject 

    //根据规范,then会返回一个全新的promise
    return new MyPromise((resolveFn, rejectFn) => {
        // 重写传入的resolve方法
        // 判断返回值
        const fulfilished = value => {
            try{
                // 接收返回值
                const res = resolve(value)

                // 判断返回值类型:promise或普通类型
                // 如果是promise,则往下执行一次then
                // 如果是普通类型,则直接执行resolveFn,保证value是最新值
                res instanceof MyPromise ? MyPromise.then(resolveFn,rejectFn) : resolveFn(res)
            }catch(e) {
                rejectFn(e)
            }
        }

        // 重写传入的reject方法
        // 判断返回值
        const rejected = reason => {
            try{
        // 接收返回值
                const res = reject(reason)

                // 判断返回值类型:promise或普通类型
                // 如果是promise,则往下执行一次then
                // 如果是普通类型,则直接执行rejectFn,保证value是最新值
                res instanceof MyPromise ? MyPromise.then(resolveFn,rejectFn) : rejectFn(res)
            }catch(e){
                rejectFn(e instanceof Error ? e.message: e)
            }

        }

        // 判断同步异步任务
        // 执行相对应的方法
        // 这里用switch方法改进
        switch(this.status) {
            case RESOLVED:
                fulfilished(this.value)
            break;

            case REJECTED:
                rejected(this.reason)
            break;

            case PENDDING:
                this.resolves.push(fulfilished)
                this.rejects.push(rejected)
              break;
        }
    })

  }
  
  catch(errorFn) {
    // 这里只需注册执行下then,传入callback就能实现
    this.then(null, errorFn);
  }

  // resolve
  static resolve(value) {
    return new MyPromise((resolveFn, rejectFn) => {
      resolveFn(value)
    })
  }

    // reject
  static reject(reason) {
    return new MyPromise((resolveFn, rejectFn) => {
      rejectFn(reason)
    })
  }

  // all
  static all(promises) {
    // 已然是返回一个promise
    return new MyPromise((resolve, reject) => {
      // 创建一个收集返回值的数组
      const result = []

      // 执行
      deepPromise(promises[0], 0 , result)

      // 返回结果
      resolve(result)

      // 这里我们用递归来实现
      // @param {MyPromise} promise 每一个promise方法
      // @param {number} index 索引
      // @param {string[]} result 收集返回结果的数组
      function deepPromise(promise, index, result) {
        // 边界判断
        // 所有执行完之后返回收集数组
        if(index > promises.length - 1) {
          return result
        }

        if(typeof promise.then === 'function') {
          // 如果是promise
          promise.then(res => {
            index++
            result.push(res)
            deepPromise(promises[index], index, result)
          }).catch(e => {
            // reject直接返回
            reject(e instanceof Error ? e.message : e)
          })
        }else {
          // 如果是普通值
          // 这里我们只做简单判断,非promise则直接当返回值处理
          index++
          result.push(promise)
          deepPromise(promises[index], index, res)
        }
      }
    })

  }

  // allSettled
  static allSettled(promises) {
      // 已然是返回一个promise
      return new MyPromise((resolve, reject) => {
        // 创建一个收集返回值的数组
        const result = []

        // 执行
        deepPromise(promises[0], 0 , result)

        // 返回结果
        resolve(result)

        // 这里我们用递归来实现
        // @param {MyPromise} promise 每一个promise方法
        // @param {number} index 索引
        // @param {string[]} result 收集返回结果的数组
        function deepPromise(promise, index, result) {
          // 边界判断
          // 所有执行完之后返回收集数组
          if(index > promises.length - 1) {
            return result
          }

          if(typeof promise.then === 'function') {
            // 如果是promise
            promise.then(res => {
              index++
              result.push({status: 'fulfilished', value: res}) // 这里推入的是对象
              deepPromise(promises[index], index, result)
            }).catch(e => {
              // reject直接返回
              index ++
              result.push({status: 'rejected', value: res}) // 这里推入的是对象
              deepPromise(promises[index], index, result)
            })
          }else {
            // 如果是普通值
            // 这里我们只做简单判断,非promise则直接当返回值处理
            index++
            result.push({status: 'fulfilished', value: res}) // 这里推入的是对象
            deepPromise(promises[index], index, res)
          }
        }
      })

    }
}

Суммировать

слишком далекоPromise A+Полный метод и реализацияpromiseТрудность заключается в пониманииthenКак быть с прозрачной передачей значения , если вы понимаете этот момент, другие методы и логика будут более естественными Если у вас есть какие-либо вопросы, вы можете указать в комментариях.