掌握JavaScript异步编程:深入解析Promise A+ 规范

Promise A+ 规范与 JavaScript Promise 对象的关系

Promise A+ 规范是一个抽象的规范,它定义了Promise对象应该如何行为,以及它们应该如何与其他异步操作交互。这个规范并没有指定Promise对象的具体实现细节,而是提供了一套通用的接口和行为模式,使得任何遵循该规范的实现都能够互相兼容。

JavaScript Promise对象是Promise A+ 规范的一个具体实现。在JavaScript中,Promise对象提供了一种编写异步代码的方式,允许您以一种更加清晰和结构化的方式处理异步操作的结果。JavaScript的Promise对象遵循Promise A+规范,这意味着它们实现了规范中定义的所有要求,包括构造函数、resolverejectthencatchallrace方法。

Promise A+ 规范

Promise 所需要具备的方法:

  1. new Promise((resolve,reject)=>{}) , 构造函数需要传入一个 fn , 并且 fn 执行时需要传入 resolve & reject 。
  2. Promise.resolve(value) , Promise.constructor 中有一个 resolve 方法, 需要将 value 值返回到 then 中 。
  3. Promise.reject(err) , 与 Promise.reject 类似。
  4. then , 对 fullfiled 状态的回调处理
  5. catch , 对 rejected 状态的回调处理
  6. Promise.all(promiseArray) , 对所有 Promise 状态都改变时进行处理
  7. Promise.race(promiseArray) , 当有一个 Promise 状态发生改变时进行处理
  8. 构造函数中需要 state 标识状态 , value 存储 fullfiled 的返回值 , reason 存储失败原因

详细规范传送门Promises/A+ (promisesaplus.com)

自己实现 Promise

Promise 类基本结构

根据 PromiseA+ 规范的定义我们可以大致建立以下的实现结构。

js 复制代码
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

class Promise{
  constructor(fn){
    this.state = PENDING;
    this.value = null;
    this.reason = null;
    this.fulfilledHandlers = [];
    this.rejectedHandlers = [];

    const resolve = ()=>{};
    const reject = ()=>{};
    try{
      fn(resolve,reject)
    }catch(err){
      reject(error)
    }
  }
  
  static resolve(){}
  static reject(){}
  static all(){}
  static race(){}
  
  then(){}
  catch(){}
}

resolve , reject 实现

resolve , reject 需要改变 state 并且异步执行 then catch 的回调

js 复制代码
const resolve = val => {
  setTimeout(() => {
    if (_this.state === PENDING) {
      _this.value = val;
      _this.state = FULFILLED;
      _this.resolveHandlers.forEach(cb =>{
          try {
              cb(val);
          }catch(e){
              reject(e);
          }
      });
    }
  }, 0);
};

const reject = err => {
  setTimeout(() => {
    if (_this.state === PENDING) {
      _this.reason = err;
      _this.state = REJECTED;
      _this.rejectedHandlers.map(cb => {
          try {
              cb(err);
          }catch(e){
              reject(e);
          }
      });
    }
  }, 0);
};

Promise.resolve = val => {
  if (val instanceof Promise) {
    return val.then(resolve, reject);
  }
  return resolve(val);
};

then catch 实现

then catch 返回一个 Promise 类型, 将状态的回调 push 到数组中 , 使用 promise.then().then(res=>{console.log(res)}) 的方式时,第一个 then 需要再次传递 onFulfilled 的值。

js 复制代码
Promise.prototype.then = (onFulfilled, onRejected) => {
  var _this = this;
  var resolveHandler, rejectHandler;
  if (typeof onFulfilled === "function") {
    resolveHandler = onFulfilled;
  }
  resolveHandler = val => val;
  rejectHandler =
    typeof onRejected === "function"
      ? onRejected
      : err => {
          throw err;
        };

  if (_this.state === PENDING) {
    _this.resolveHandlers.push(onFulfilled);
    _this.rejectedHandlers.push(onRejected);
    return;
  }

  return new Promise((resolve, reject) => {
    if (_this.state === FULFILLED) {
      resolve(_this.value);
    }else{
      reject(_this.reason)
    }
  });
};

全量代码

js 复制代码
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class MyPromise {
  constructor(executor) {
    this.state = PENDING;
    this.value = null;
    this.reason = null;
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];

    const resolve = (value) => {
      if (this.state === PENDING) {
        this.state = FULFILLED;
        this.value = value;
        this.onFulfilledCallbacks.forEach((fn) => fn());
      }
    };

    const reject = (reason) => {
      if (this.state === PENDING) {
        this.state = REJECTED;
        this.reason = reason;
        this.onRejectedCallbacks.forEach((fn) => fn());
      }
    };

    try {
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }

  then(onFulfilled, onRejected) {
    onFulfilled =
      typeof onFulfilled === 'function' ? onFulfilled : (value) => value;
    onRejected =
      typeof onRejected === 'function'
        ? onRejected
        : (err) => {
            throw err;
          };

    let promise2 = new MyPromise((resolve, reject) => {
      if (this.state === FULFILLED) {
        setTimeout(() => {
          try {
            let x = onFulfilled(this.value);
            this.resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        });
      }
      if (this.state === REJECTED) {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason);
            this.resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        });
      }

      if (this.state === PENDING) {
        this.onFulfilledCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onFulfilled(this.value);
              this.resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          });
        });

        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason);
              this.resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          });
        });
      }
    });

    return promise2;
  }

  catch(errCallback) {
    return this.then(null, errCallback);
  }

  resolvePromise(promise2, x, resolve, reject) {
    if (x === promise2) {
      return reject(new TypeError('Chaining cycle detected for promise'));
    }
    let called;
    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
      try {
        let then = x.then;
        if (typeof then === 'function') {
          then.call(
            x,
            (y) => {
              if (called) return;
              called = true;
              this.resolvePromise(promise2, y, resolve, reject);
            },
            (err) => {
              if (called) return;
              called = true;
              reject(err);
            }
          );
        } else {
          resolve(x);
        }
      } catch (e) {
        if (called) return;
        called = true;
        reject(e);
      }
    } else {
      resolve(x);
    }
  }

  static resolve(value) {
    return value instanceof MyPromise
      ? value
      : new MyPromise((resolve) => resolve(value));
  }

  static reject(reason) {
    return new MyPromise((_, reject) => reject(reason));
  }

  static all(promiseArr) {
    let result = [];
    let promiseCount = 0;
    return new MyPromise((resolve, reject) => {
      promiseArr.forEach((promise, i) => {
        MyPromise.resolve(promise).then(
          (val) => {
            result[i] = val;
            promiseCount++;
            if (promiseCount === promiseArr.length) {
              resolve(result);
            }
          },
          (reason) => {
            reject(reason);
          }
        );
      });
    });
  }

  static race(promiseArr) {
    return new MyPromise((resolve, reject) => {
      promiseArr.forEach((promise, i) => {
        MyPromise.resolve(promise).then(
          (val) => {
            resolve(val);
          },
          (reason) => {
            reject(reason);
          }
        );
      });
    });
  }
}

测试用例

js 复制代码
// 1. 测试 Promise.resolve
let promise1 = MyPromise.resolve(3);
promise1.then((value) => console.log(value)); // 应该打印出 3

// 2. 测试 Promise.reject
let promise2 = MyPromise.reject('Error occurred');
promise2.catch((reason) => console.log(reason)); // 应该打印出 "Error occurred"

// 3. 测试 MyPromise.then 和异步
let promise3 = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('Hello');
  }, 1000);
});
promise3.then((value) => console.log(value)); // 隔1秒后应该打印出 "Hello"

// 4. 测试 catch 错误处理
let promise4 = new MyPromise((resolve, reject) => {
  throw new Error('some error');
});
promise4.catch((error) => console.log(error.toString())); // 应该打印出 "Error: some error"

// 5. 测试 Promise.all
let promise5 = MyPromise.all([
  Promise.resolve(4),
  Promise.resolve(5),
  Promise.resolve(6),
]);
promise5.then((values) => console.log(values)); // 应打印出 [4, 5, 6]

// 6. 测试 Promise.race
let promise6 = MyPromise.race([
  MyPromise.resolve(7),
  MyPromise.reject('Error'),
  MyPromise.resolve(8),
]);
promise6.then((value) => console.log(value)); // 应打印出 7


//7. 
let promise7 = new MyPromise((resolve, reject) => {
  resolve('promise7')
});
promise7.then((value) =>{
  console.log(value)
  return MyPromise.resolve('promise7 ppp')
}).then(val=>console.log(val)); // 隔1秒后应该打印出 "Hello"

总结

异步编程是JavaScript中处理非阻塞操作的关键,它允许程序在等待某些操作(如网络请求、文件读写等)完成时继续执行其他任务。Promise对象的引入,解决了传统回调函数(callback)可能带来的"回调地狱"问题,通过链式调用和更好的错误处理机制,使得异步流程控制变得更加直观和易于管理。

相关推荐
GISer_Jing2 小时前
前端面试通关:Cesium+Three+React优化+TypeScript实战+ECharts性能方案
前端·react.js·面试
落霞的思绪3 小时前
CSS复习
前端·css
咖啡の猫5 小时前
Shell脚本-for循环应用案例
前端·chrome
百万蹄蹄向前冲7 小时前
Trae分析Phaser.js游戏《洋葱头捡星星》
前端·游戏开发·trae
朝阳5818 小时前
在浏览器端使用 xml2js 遇到的报错及解决方法
前端
GIS之路8 小时前
GeoTools 读取影像元数据
前端
ssshooter9 小时前
VSCode 自带的 TS 版本可能跟项目TS 版本不一样
前端·面试·typescript
你的人类朋友9 小时前
【Node.js】什么是Node.js
javascript·后端·node.js
Jerry9 小时前
Jetpack Compose 中的状态
前端
dae bal10 小时前
关于RSA和AES加密
前端·vue.js