掌握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)可能带来的"回调地狱"问题,通过链式调用和更好的错误处理机制,使得异步流程控制变得更加直观和易于管理。

相关推荐
前端西瓜哥8 分钟前
贝塞尔曲线算法:求贝塞尔曲线和直线的交点
前端·算法
又写了一天BUG9 分钟前
npm install安装缓慢及npm更换源
前端·npm·node.js
cc蒲公英22 分钟前
Vue2+vue-office/excel 实现在线加载Excel文件预览
前端·vue.js·excel
Java开发追求者23 分钟前
在CSS中换行word-break: break-word和 word-break: break-all区别
前端·css·word
好名字082127 分钟前
monorepo基础搭建教程(从0到1 pnpm+monorepo+vue)
前端·javascript
pink大呲花35 分钟前
css鼠标常用样式
前端·css·计算机外设
Flying_Fish_roe35 分钟前
浏览器的内存回收机制&监控内存泄漏
java·前端·ecmascript·es6
c#上位机44 分钟前
C#事件的用法
java·javascript·c#
小小竹子1 小时前
前端vue-实现富文本组件
前端·vue.js·富文本
万物得其道者成1 小时前
React Zustand状态管理库的使用
开发语言·javascript·ecmascript