Skip to content

手写Promise.all

js
function myPromiseAll(promises) {
    // 返回一个新的 Promise
    return new Promise((resolve, reject) => {
        // 如果输入的不是数组,直接返回一个拒绝的 Promise
        if (!Array.isArray(promises)) {
            return reject(new TypeError('Argument must be an array'));
        }
        
        // 保存所有 Promise 结果
        let results = [];
        // 记录已完成的 Promise 数量
        let completed = 0;
        // 记录总 Promise 数量
        const total = promises.length;

        // 如果数组为空,直接返回一个已解析的 Promise
        if (total === 0) {
            return resolve(results);
        }

        // 遍历所有传入的 Promise
        promises.forEach((promise, index) => {
            // 确保每个项都是 Promise 实例
            Promise.resolve(promise)
                .then(value => {
                    // 记录每个 Promise 的结果
                    results[index] = value;
                    completed += 1;

                    // 如果所有 Promise 都已完成,返回结果数组
                    if (completed === total) {
                        resolve(results);
                    }
                })
                .catch(error => {
                    // 如果任何一个 Promise 被拒绝,返回拒绝的 Promise
                    reject(error);
                });
        });
    });
}

// 使用示例
const p1 = Promise.resolve(1);
const p2 = Promise.resolve(2);
const p3 = new Promise((resolve) => setTimeout(resolve, 100, 3));

myPromiseAll([p1, p2, p3])
    .then(results => console.log(results)) // [1, 2, 3]
    .catch(error => console.error(error));

【代码题】大数相加

输入:num1 = '1234567890', num2 = '987654321' 输出:'2222222211'

js
//  这是一道比较简单的题目,主要就是模拟加法进位的实现
const add = (num1, num2) => {
    const n = Math.max(num1.length, num2.length);
    //  逆序一下,从最后一位开始相加,同时前面位数不够的补0
    const arr1 = num1.split('').reverse();
    const arr2 = num2.split('').reverse();
    const result = [];
    //  进位的标识,只有进1位和不进位两种情况
    let temp = 0;
    for (let i = 0; i < n; i++) {
        const a = +(arr1[i] || 0);
        const b = +(arr2[i] || 0);
        let current = a + b + temp;
        if (current >= 10) {
            temp = 1;
            current -= 10;
        } else {
            temp = 0;
        }
        result.push(current);
    }
    //  考虑到进位导致的量级增加情况,需要额外处理
    if (temp) {
        result.push(temp);
    }
    return result.reverse().join('');
}

【代码题】实现一个同步的sleep方法

调用方式:(new LazyLog()).log(1).sleep(1000).log(2) 输出:先输出1,延迟1秒后输出2

js
//  一开始我的想法是通过Promise去实现sleep,后来发现Promise的话无法满足直接的链式调用方式
//  面完之后下来查了一下,发现可以通过死循环去实现同步的sleep,但是这种方式对性能有极大的影响
//  在某些环境下会执行报错,后来又去查了一下开源的sleep库https://www.npmjs.com/package/sleep
//  发现它的最终也是通过c++原生代码编译成node模块来实现的,所以这个问题有没有啥更好的答案呢🤔
class LazyLog {
  log(str) {
    console.log(str)
    return this;
  }
  
  // async sleep(delay) {
  //   await new Promise(resolve => setTimeout(() => resolve(), delay));
  //   return this;
  // }
  
  sleep(delay) {
    const current = Date.now();
    while (Date.now() - current < delay) {
      // 什么都不做
    }
    return this;
  }
}

【代码题】查找有序数组中数字最后一次出现的位置

输入:nums = [5,7,7,8,8,10], target = 8 输出:4

js
//  最简答的方式就是直接遍历然后根据有序的条件找到当前值等于目标且下一个值不等于目标的结果
//  写出来之后面试官问了时间复杂度,这个就是单层循环的 O(N),最坏情况就是刚好最后一个值是目标值
const findLast = (nums, target) => {
    for (let i = 0; i < nums.length; i++) {
      if (target === nums[i] && target !== nums[i + 1]) {
        return i;
      }
    }
    return -1;
};

//  问有没有更好的方式,就想到了二分查找,对于已经有序的数组,只需要通过双指针不断更新左右边界位置就行
//  二分法最主要的就是寻找二分结束的边界条件,这里选择所有的查找最后都只剩两个值
//  然后对这两个值再额外判断一下是否符合结果
//  面试官继续追问二分法的时间复杂度,这个我有点懵,不过考虑跟递归差不多,所以就回答了O(logN),应该是没错
//  二分查找最坏的情况是刚好第一个值或者最后一个值,或者中间值是目标值
const findLast2 = (nums, target) => {
    let left = 0;
    let right = nums.length - 1;
    while (right > left + 1) {
        const mid = Math.floor((left + right) / 2);
        if (nums[mid] > target) {
            right = mid - 1;
        } else {
            left = mid;
        }
    }
    if (nums[right] === target) {
        return right;
    }
    if (nums[left] === target) {
        return left;
    }
    return -1;
};