鸿蒙中 Promise.all()的实现原理(三)

鸿蒙中Promise.all()实现原理详解

本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新

一、Promise.all 的核心特性

1. 标准行为

// 输入:Promise数组
// 输出:新的Promise
Promise.all([promise1, promise2, ...])
  .then(values => {
    // 所有Promise成功时触发
    // values按输入顺序排列的结果数组
  })
  .catch(reason => {
    // 任意一个Promise失败时立即触发
  });

2. 关键特性

特性描述
全部成功返回结果数组(保持原始顺序)
快速失败任一失败立即拒绝
非Promise值直接作为结果返回
空输入返回已解决的Promise

二、手写实现示例

1. 基础实现

function myPromiseAll(promises) {
  // 1. 返回新的Promise
  return new Promise((resolve, reject) => {
    // 2. 处理非数组输入
    if (!Array.isArray(promises)) {
      return reject(new TypeError('Arguments must be an array'));
    }

    const results = [];
    let completedCount = 0;
    const total = promises.length;

    // 3. 处理空数组情况
    if (total === 0) {
      return resolve(results);
    }

    // 4. 遍历处理每个Promise
    promises.forEach((promise, index) => {
      // 5. 确保处理非Promise值(用Promise.resolve包装)
      Promise.resolve(promise)
        .then(value => {
          // 6. 按顺序存储结果
          results[index] = value;
          completedCount++;

          // 7. 检查是否全部完成
          if (completedCount === total) {
            resolve(results);
          }
        })
        .catch(reason => {
          // 8. 任一失败立即拒绝
          reject(reason);
        });
    });
  });
}

2. 增强版实现

function enhancedPromiseAll(promises, options = {}) {
  return new Promise((resolve, reject) => {
    /* 新增功能:
       - 超时控制
       - 进度回调
       - 部分成功记录
    */
    
    const {
      timeout,
      onProgress
    } = options;

    // ...(基础实现逻辑)

    // 超时控制
    if (timeout) {
      const timer = setTimeout(() => {
        reject(new Error(`Operation timed out after ${timeout}ms`));
      }, timeout);

      // 清理定时器
      promises.finally(() => clearTimeout(timer));
    }

    // 进度回调
    if (onProgress) {
      promises.forEach(promise => {
        promise.finally(() => {
          completedCount++;
          onProgress(completedCount / total);
        });
      });
    }
  });
}

三、实现原理图解

四、关键实现技巧

1. 顺序保持机制

// 使用索引而非push保证顺序
promises.forEach((promise, index) => {
  Promise.resolve(promise)
    .then(value => {
      results[index] = value; // 关键点:按原始位置存储
      // ...
    });
});

2. 完成计数优化

// 原子性计数方案
let completedCount = 0;

function checkCompletion() {
  // 使用闭包避免竞态条件
  if (++completedCount === promises.length) {
    resolve(results);
  }
}

3. 非Promise处理

// 统一处理方案
Promise.resolve(item).then(...)
// 等价于:
if (item instanceof Promise) {
  item.then(...)
} else {
  Promise.resolve(item).then(...)
}

五、边界情况测试

测试用例集

// 1. 基础功能
myPromiseAll([
  Promise.resolve(1),
  Promise.resolve(2)
]).then(console.log); // [1, 2]

// 2. 快速失败
myPromiseAll([
  Promise.resolve(1),
  Promise.reject('error')
]).catch(console.log); // 'error'

// 3. 非Promise值
myPromiseAll([
  1,
  Promise.resolve(2)
]).then(console.log); // [1, 2]

// 4. 空数组
myPromiseAll([]).then(console.log); // []

// 5. 顺序保证
myPromiseAll([
  new Promise(r => setTimeout(() => r(1), 100)),
  Promise.resolve(2)
]).then(console.log); // [1, 2]

六、与原生实现的差异

特性手写实现原生实现
迭代器支持仅数组支持可迭代对象
微任务调度简单实现严格遵循规范
性能优化基础引擎级优化
错误类型简单错误特定错误类型

七、进阶实现建议

1. 支持可迭代对象

function iterablePromiseAll(iterable) {
  // 将iterable转为数组
  const promises = Array.from(iterable);
  // ...其余逻辑相同
}

 2. 取消功能扩展

function cancellablePromiseAll(promises) {
  let isCancelled = false;
  
  const wrapped = promises.map(p => 
    p.catch(err => {
      if (isCancelled) throw new Error('Cancelled');
      throw err;
    })
  );
  
  const result = myPromiseAll(wrapped);
  
  result.cancel = () => {
    isCancelled = true;
  };
  
  return result;
}

 3. 性能优化版

function fastPromiseAll(promises) {
  // 预分配数组
  const results = new Array(promises.length);
  let remaining = promises.length;
  
  return new Promise((resolve, reject) => {
    promises.forEach((promise, i) => {
      Promise.resolve(promise).then(
        value => {
          results[i] = value;
          if (--remaining === 0) resolve(results);
        },
        reject
      );
    });
  });
}

八、实际应用示例

1. 并行请求合并

async function fetchUserData(userId) {
  const [profile, orders, messages] = await myPromiseAll([
    fetch(`/users/${userId}`),
    fetch(`/orders?user=${userId}`),
    fetch(`/messages/${userId}`)
  ]);
  
  return { profile, orders, messages };
}

2. 批量文件操作

function batchReadFiles(files) {
  return myPromiseAll(
    files.map(file => 
      new Promise((resolve, reject) => {
        fs.readFile(file, (err, data) => {
          err ? reject(err) : resolve(data);
        });
      })
    )
  );
}

总结

  1. 核心逻辑:通过计数器和数组索引维护顺序
  2. 错误处理:任一失败立即终止
  3. 边界处理:空数组、非Promise值等特殊情况
  4. 性能考量:避免不必要的内存分配
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值