js手写题:
# js手写题:
# 排序算法
# 冒泡排序
时间复杂度:O(N^2); 空间复杂度:O(1)
思路:
比较相邻元素:从数组的第一个元素开始,依次比较相邻的两个元素。
- 如果前一个比后一个大,就交换它们的位置。
一轮比较完成后:数组的最后一个元素一定是最大的,就像“气泡”一样被“冒”到了最上面。
重复上述过程:
- 每一轮都会将剩余未排序部分的最大值冒到末尾。
- 直到所有元素都有序。
function BubbleSort(arr) {
if(arr == null || arr.length <= 0 ){
return []
}
let len = arr.length
for(let end = len - 1; end > 0; end--){
for(let i = 0; i < end; i ++){
if(arr[i] > arr[i+1] {
swap(arr, i , i +1)
}
}
return arr
}
function swap(arr, i , j) {
// var temp = arr[i];
// arr[i] = arr[j];
// arr[j] = temp;
//交换也可以用异或运算符
arr[i] = arr[i] ^ arr[j];
arr[j] = arr[i] ^ arr[j];
arr[i] = arr[i] ^ arr[j];
}
举个例子
对 [5, 2, 4, 1] 进行冒泡排序:
- 第一轮:
- 5 和 2 比,交换 →
[2, 5, 4, 1] - 5 和 4 比,交换 →
[2, 4, 5, 1] - 5 和 1 比,交换 →
[2, 4, 1, 5](最大数 5 冒到最后)
- 5 和 2 比,交换 →
- 第二轮:
- 2 和 4 比,不交换 →
[2, 4, 1, 5] - 4 和 1 比,交换 →
[2, 1, 4, 5] - (次大数 4 冒到倒数第二位)
- 2 和 4 比,不交换 →
- 第三轮:
- 2 和 1 比,交换 →
[1, 2, 4, 5]✅ 有序
- 2 和 1 比,交换 →
# 选择排序
时间复杂度:O(N^2); 空间复杂度:O(1)
思路:
- 从未排序部分中找到最小值(或最大值)
- 把这个最小值放到已排序部分的尾部
- 重复1和2,直到所有元素有序
function SelectionSort(arr) {
if(arr == null || arr.length < 0) {
return []
}
for(let i = 0; i < arr.length - 1 ;i++){
let minIndex = i
for(let j = i + 1; j < arr.lenght; j++){
// 找最小的值
minIndex = arr[j] < arr[minIndex] ? j :minIndex
}
//把这个最小值放到已排序部分的尾部
swap(arr, i , minIndex)
}
return arr
}
举个例子
对 [5, 2, 4, 1] 进行选择排序(升序):
- 第一轮:从
[5, 2, 4, 1]中找最小值1,放到第一个位置 →[1, 2, 4, 5] - 第二轮:从剩余
[2, 4, 5]中找最小值2,保持不动 →[1, 2, 4, 5] - 第三轮:从
[4, 5]中找最小值4,保持不动 →[1, 2, 4, 5] - 第四轮:最后只剩
5,排序完成。 ✅
# 插入排序
时间复杂度:O(N^2); 空间复杂度:O(1)
思路:
- 把数组分为已排序区和未排序区
- 从未排序区中依次取出一个数,插入到已排序区的正确位置(保持有序)
- 重复直到未排序区为空
有点像打扑克牌时,手里的一叠牌总是边摸牌边整理
/**
* 插入排序
* 思路:将数组分为“已排序区间”和“未排序区间”。
* 从未排序区间中取出一个数,插入到已排序区间的正确位置。
*
* 时间复杂度:
* - 最好情况:O(n) (原本有序)
* - 平均/最坏情况:O(n^2)
* 空间复杂度:O(1) 原地排序
* 稳定性:稳定(相等元素不会交换顺序)
*/
function insertSort(arr) {
// 边界处理:空数组或单元素数组,直接返回
if (arr == null || arr.length <= 0) {
return [];
}
var len = arr.length;
// 从第二个元素开始(默认第一个元素是已排序区间)
for (var i = 1; i < len; i++) {
// 从当前位置向前扫描,找到合适的插入位置
for (var j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {
// 如果前一个元素比当前元素大,则交换
swap(arr, j, j + 1);
}
}
return arr;
}
function swap(arr, i, j) {
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
举个例子
对 [5, 2, 4, 1] 做插入排序:
- 初始:已排序区
[5],未排序区[2, 4, 1] - 取
2插入 →[2, 5] | [4, 1] - 取
4插入 →[2, 4, 5] | [1] - 取
1插入 →[1, 2, 4, 5] | []✅ 排序完成
# 归并排序
思路:
分解(Divide)
- 不断把数组对半拆分,直到每个子数组只剩 1 个元素(天然有序)。
合并(Conquer)
- 从最小的子数组开始,两两合并成有序数组。
- 在合并过程中,利用两个子数组都已经有序的性质,依次比较并放入新数组。
「先分成碎片,再把碎片有序地拼起来」
/**
* 归并排序(循环版,自底向上)
* 思路:
* 1. 一开始将每个元素看作长度为1的有序子数组。
* 2. 每次按子数组长度(1,2,4,8...)两两合并。
*/
function mergeSortIterative(arr) {
if (!arr || arr.length <= 1) return arr;
const n = arr.length;
let step = 1;
// step 表示子数组的长度,每次翻倍
while (step < n) {
for (let i = 0; i < n; i += 2 * step) {
const left = arr.slice(i, i + step);
const right = arr.slice(i + step, i + 2 * step);
const merged = merge(left, right);
// 把合并好的数组放回原数组对应位置
for (let k = 0; k < merged.length; k++) {
arr[i + k] = merged[k];
}
}
step *= 2; // 子数组长度翻倍
}
return arr;
}
/**
* 归并排序(递归版,自顶向下)
* 思路:
* 1. 分:不断把数组二分,直到子数组长度为 1。
* 2. 治:合并两个有序子数组。
*/
function mergeSortRecursive(arr) {
if (!arr || arr.length <= 1) return arr;
// 拆分:取中点
const mid = Math.floor(arr.length / 2);
const left = mergeSortRecursive(arr.slice(0, mid));
const right = mergeSortRecursive(arr.slice(mid));
// 合并:两个有序数组
return merge(left, right);
}
/**
* 合并两个有序数组
*/
function merge(left, right) {
const result = [];
let i = 0, j = 0;
// 逐个比较,较小的先放入结果
while (i < left.length && j < right.length) {
if (left[i] <= right[j]) {
result.push(left[i++]);
} else {
result.push(right[j++]);
}
}
// 剩余部分直接拼接
return result.concat(left.slice(i)).concat(right.slice(j));
}
# 堆排序
堆排序的核心思想是利用**完全二叉树结构的堆(最大堆/最小堆)**来实现排序。
堆的定义
- 最大堆:每个父节点的值都大于或等于子节点。
- 最小堆:每个父节点的值都小于或等于子节点。 堆排序通常用最大堆,因为我们每次把最大值“冒”到根(arr[0])。
堆排序步骤
- 建堆:先把数组调整成一个最大堆。
- 排序过程:
- 把堆顶(最大值)和最后一个元素交换。
- 堆的有效长度减一(相当于把最后一个元素放到正确位置)。
- 重新调整剩余部分为最大堆。
- 重复这个过程,直到堆的大小为 1。
时间复杂度
- 建堆:O(n)
- 每次调整堆:O(log n),执行 n-1 次
- 总复杂度:O(n log n)
- 堆排序是原地排序,空间复杂度 O(1)。
假设数组 [5, 2, 9, 1, 3]
- 建最大堆 →
[9, 5, 2, 1, 3] - 交换堆顶和最后一个 →
[3, 5, 2, 1, 9] - 调整堆 →
[5, 3, 2, 1, 9] - 再交换堆顶和倒数第二个 →
[1, 3, 2, 5, 9] - 调整堆 →
[3, 1, 2, 5, 9] - 如此继续,最终得到
[1, 2, 3, 5, 9]
function heapSort(arr) {
if (!arr || arr.length <= 1) return arr
let n = arr.length
// 1. 建堆(从最后一个非叶子节点开始调整)
for (let i = Math.floor(n / 2) - 1; i >= 0; i--) {
heapify(arr, n, i)
}
// 2. 不断取出堆顶元素放到最后,然后调整剩余部分
for (let i = n - 1; i > 0; i--) {
swap(arr, 0, i) // 最大值交换到末尾
heapify(arr, i, 0) // 重新调整堆(只调整前 i 个)
}
return arr
}
// 调整堆函数
function heapify(arr, heapSize, i) {
let largest = i
let left = 2 * i + 1
let right = 2 * i + 2
// 左子节点比当前大
if (left < heapSize && arr[left] > arr[largest]) {
largest = left
}
// 右子节点比当前大
if (right < heapSize && arr[right] > arr[largest]) {
largest = right
}
// 如果最大值不是父节点,交换并递归调整
if (largest !== i) {
swap(arr, i, largest)
heapify(arr, heapSize, largest)
}
}
# 快速排序
快速排序是一种 分治法 排序算法
选择一个基准值(pivot)(通常取数组第一个或最后一个元素)。
分区(partition):把数组分成两部分:
- 小于基准值的放左边
- 大于基准值的放右边
递归排序 左右两部分。
合并结果(左右部分 + 基准值)。
平均时间复杂度:O(n log n)
最坏时间复杂度:O(n²)(当数组本身有序且选基准值不佳时)
空间复杂度:O(log n)(递归栈空间)
排序 [5, 2, 9, 1, 3]:
- 选
3为基准:分成[2, 1] 3 [5, 9] - 左边
[2, 1]→ 排序 →[1, 2] - 右边
[5, 9]→ 排序 →[5, 9] - 合并 →
[1, 2, 3, 5, 9]
写法 1:简单版(递归 + 额外数组)
function quickSort(arr) {
if (arr.length <= 1) return arr
let pivot = arr[arr.length - 1] // 选择最后一个元素作为基准
let left = []
let right = []
for (let i = 0; i < arr.length - 1; i++) {
if (arr[i] < pivot) {
left.push(arr[i])
} else {
right.push(arr[i])
}
}
return [...quickSort(left), pivot, ...quickSort(right)]
}
// 测试
console.log(quickSort([5, 2, 9, 1, 3])) // [1, 2, 3, 5, 9]
写法 2:原地交换(更高效)
function quickSort(arr, left = 0, right = arr.length - 1) {
if (left < right) {
let pivotIndex = partition(arr, left, right)
quickSort(arr, left, pivotIndex - 1) // 排序左边
quickSort(arr, pivotIndex + 1, right) // 排序右边
}
return arr
}
function partition(arr, left, right) {
let pivot = arr[right] // 基准值
let i = left - 1
for (let j = left; j < right; j++) {
if (arr[j] <= pivot) {
i++
swap(arr, i, j)
}
}
swap(arr, i + 1, right)
return i + 1
}
// 测试
console.log(quickSort([5, 2, 9, 1, 3])) // [1, 2, 3, 5, 9]
# 数组去重
有7种方法(set、)
# 利用Set()+Array.from()
Set对象:是值的集合,你可以按照插入的顺序迭代它的元素。 Set中的元素只会出现一次,即Set中的元素是唯一的。Array.from()方法:对一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例。
const result = Array.from(new Set(arr))
// 或者
const result = arr => [...new Set(arr)]
NaN和undefined类型去重也是有效的,是因为NaN和undefined都可以被存储在Set中,NaN之间被视为相同的值(尽管在js中:NaN !== NaN)。
# 利用两层循环+数组的splice方法
通过两层循环对数组元素进行逐一比较,然后通过splice方法来删除重复的元素。此方法对NaN是无法进行去重的,因为进行比较时NaN !== NaN。
function removeDuplicate(arr) {
let len = arr.length
for(let i = 0; i < len; i++){
for(let j = i+1; j < len; j++){
if(arr[i] === arr[j]) {
arr.splice(j,1)
len-- // 减少循环次数提高性能
j-- // 保证j的值自加后不变
}
}
}
return arr
}
# 利用数组的indexOf方法
新建一个空数组,遍历需要去重的数组,将数组元素存入新数组中,存放前判断数组中是否已经含有当前元素,没有则存入。此方法也无法对NaN去重。
indexOf()方法:返回调用它的String对象中第一次出现的指定值的索引,从fromIndex处进行搜索。如果未找到该值,则返回 -1。
function removeDuplicate(arr) {
let result = []
for(let i = 0; i < arr.length; i++){
if(!result.indexOf(arr[i])){
result.push(arr[i])
}
}
return result
}
# 利用数组的includes方法
此方法逻辑与indexOf方法去重异曲同工,只是用includes方法来判断是否包含重复元素。
includes()方法:用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回true,否则返回false。
function removeDuplicate(arr) {
let result = []
arr.forEach(item => {
if(!result.includes(item)){
result.push(item)
}
})
return result
}
注意:为什么
includes能够检测到数组中包含NaN,其涉及到includes底层的实现在进行判断是否包含某元素时会调用
sameValueZero方法进行比较,如果为NaN,则会使用isNaN()进行转化。
# 利用数组的filter()+indexOf()
filter方法会对满足条件的元素存放到一个新数组中,结合indexOf方法进行判断。
filter()方法:会创建一个新数组,其包含通过所提供函数实现的测试的所有元素。
function removeDuplicate(arr) {
return arr.filter((item, index) => {
return arr.indexOf(item) === index
})
}
# 利用Map()
Map对象是JavaScript提供的一种数据结构,结构为键值对形式,将数组元素作为map的键存入,然后结合
has()和set()方法判断键是否重复。
Map对象:用于保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者原始值)都可以作为一个键或一个值。
function removeDuplicate(arr) {
const map = new Map()
const result = []
arr.forEach(item => {
if(!map.has(item)){
map.set(item)
result.push(item)
}
})
return result
}
注意:使用Map()也可对NaN去重,原因是Map进行判断时认为NaN是与NaN相等的,剩下所有其它的值是根据 === 运算符的结果判断是否相等。
# 利用对象
其实现思想和
Map()是差不多的,主要是利用了对象的属性名不可重复这一特性。
function removeDuplicate(arr) {
const result = []
const obj = {}
arr.forEach(item => {
if(!obj[item]){
result.push(item)
obj[item] = true
}
})
return result
}
# 版本号排序
题目: 输入一组版本号,输出从大到小的排序 输入: [‘2.1.0.1’, ‘0.402.1’, ‘10.2.1’, ‘5.1.2’, ‘1.0.4.5’] 输出: [‘10.2.1’, ‘5.1.2’, ‘2.1.0.1’, ‘1.0.4.5’, ‘0.402.1’]
思路:肯定用的是排序,选用split分割,遍历判断:
(1)其中一个arr[i]为undefined说明前面判断的都相同,则直接判断arr.length;
(2)arr[i]相同则continue
(3)不同则直接return arr2[i]-arr1[i] 不用转成number
function versionSort(arr){
arr.sort((a,b) => {
let i = 0;
let arr1 = a.split(".")
let arr2 = b.split(".")
while(true){
// 取出相同位置的数字
const s1 = arr1[i];
const s2 = arr2[i];
i++;
if(s1 === undefined || s2 === undefined){
// 比较长度
return s1.length - s2.length
}
if(s1 === s2) continue
return s1 - s2
}
})
}
# map计数类型
使用对象: 涉及到需要计算数组或字符串中某个元素的数量时
步骤: 一般定义一个map,遍历数组或字符串,利用counter[s[i]] ? counter[s[i]]++ : (counter[s[i]] = 1); 计数
# 3.1存在重复元素
题目描述如下:
给定一个整数数组,判断是否存在重复元素。如果存在一值在数组中出现至少两次,函数返回 true 。如果数组中每个元素都不相同,则返回 false
function judge(num) {
let map = new Map()
for(let i of num){
if(map.has(i)){
return true
}else{
map.set(i,1)
}
}
return false
}
# 字符串第一个唯一字符
题目:给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。
思路:用map。 定义一个对象。 遍历字符串,元素出现一次就+1 map[s] = (map[s] || 0) + 1;.最后再次遍历字符串,找到第一次map[str[i]] === 1
function firstUniqChar(str) {
let map = {}
for(let s of str) {
map[s] = (map[s] || 0) + 1
}
for(let i = 0; i < str.length; i++){
if(map[str[i]] === 1) return i
}
return -1
}
# map存储
# 4.1:两数之和
题目:给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
思路:用map。 每遍历一个元素,看看map 中是否存在target-nums[i],存在则返回[map.get(element),i],不存在则加入当前元素到map
var twoSum = function(nums, target) {
let map = new Map()
for(let i= 0; i < nums.length; i++){
if(map.has(target - nums[i]){
return [map.get(target - nums[i]),i]
} else{
map.set(nums[i], i)
}
}
return []
}
# 4.2:两数组交集
题目: 给定两个数组,编写一个函数来计算它们的交集。(交集里的元素互不相同) 思路:定义一个对象map={}。遍历arr1,设置map[arr1[i]]=true。遍历arr2,对每个map[arr2[i]]==true的元素记录,并设置当前map[arr2[i]]=false避免重复。
// 交集就是两个集合共同的
function intersection(arr1, arr2) {
let map = {}
let res = {}
for (let i = 0; i < arr1.length; i++) {
map[arr1[i]] = true
}
for (let i = 0 ; i < arr2.length; i++) {
if(map[arr2[i]]){
res.push(arr2[i])
map[arr2[i]] = false // 避免重复
}
}
return res
}
用filter:
function intersection(arr1, arr2) {
const duplicates = arr1.filter(item => arr2.includes(item));
return duplicates;
}
# 递归
适用对象: 一个大问题很复杂,但是可以拆分为很多个小问题,只要小问题都解决了,那么大问题就解决了。搞个数组(一维、二维),从最小的问题一直计算到最大的问题。
而递归是大问题到小问题且不需要记录过程值
一般解题步骤: 第一步骤:定义数组元素的含义 第二步骤:找出数组元素之间的关系式 : 利用 dp[n-1],dp[n-2]……dp[1], 来推出 dp[n] ,一般找dp[i-1],dp[i-2]与dp[i]的关系 第三步骤:找出初始值 :dp[0]、dp[1]、dp[2]等 总结一下就是:写出递归公式,找到终止条件
# promise封装
# promise封装setTimeout,间隔打印1 1 1
思路解析
- 封装 setTimeout 成一个 Promise,叫
delay,可以用来await。 - 使用
async/await,在循环里顺序等待delay。 - 这样就能实现“每隔一段时间打印一次”。
代码
async/await版本
function delayPrint(timeout) {
return new Promise(resolve => {
setTimeout(() => {
console.log('1');
resolve();
}, timeout);
});
}
// 按间隔打印 1 1 1
async function printOnInterval() {
for(let i = 0; i < 3; i++){
await delayPrint(1000);
}
}
printOnInterval()
用 Promise 链式调用:
function delayedLog(message, delay) {
return new Promise((resolve) => {
setTimeout(() => {
console.log(message);
resolve();
}, delay);
});
}
function printOne() {
delayedLog('1', 1000)
.then(() => delayedLog('1', 1000))
.then(() => delayedLog('1', 1000));
}
printOne();
# promsie实现每隔一秒输出1,2,3
# 手搓promise
我会先写一个 class MyPromise,在构造函数里定义三个东西:
- 一个
state初始是"pending"; - 一个
value保存成功结果; - 一个
reason保存失败原因; 同时还会有两个数组,onFulfilled和onRejected,用来存放回调。
接着我会写 resolve 和 reject 两个函数:
resolve会判断如果还是pending,就把状态改成"fulfilled",把值保存下来,然后把成功回调依次执行。reject也是类似逻辑,把状态改成"rejected",保存 reason,然后把失败回调依次执行。
然后 then 方法我会写在原型上:
- 它接收两个参数
onFulfilled和onRejected,我会做一个容错,如果不是函数就做透传。 then会返回一个新的MyPromise,这是为了支持链式调用。- 在新的 Promise 里,如果当前状态已经是 fulfilled,就用
setTimeout异步执行onFulfilled,把结果交给下一个 Promise 的 resolve。 - 如果是 rejected 就执行
onRejected,同样把结果交给下一个 Promise。 - 如果还在 pending,就把这两个回调暂存进数组,等到状态改变再异步执行。
这样基本上一个最小可用的 Promise 就完成了,能支持状态管理、回调存储和链式调用。
// ====================== 手搓一个promise======================
// 要点:Promise就是一个状态机,处理异步回调问题
// 三个关键点就是
// 状态管理,回调,链式调用
class MyPromise {
constructor(executor) {
this.state = 'pending' // 初始状态
this.value = undefined // 成功的值
this.reason = undefined // 失败的值
this.onFulfilled = [] // 保证 p 不是 Promise 时也能处理
this.onRejected = [] // 存放失败的回调
// resolve 函数,用来修改状态为 fulfilled
const resolve = (value) => {
if (this.state === 'pending') {
this.state = 'fulfulled' // 只能由 pending -> fulfilled
this.value = value
// 执行所有成功回调(可能有多个 then 注册)
this.onFulfilled.forEach((fn) => fn())
}
}
// reject 函数,用来修改状态为 rejected
const reject = (reason) => {
if (this.state === 'pending') {
this.state = 'rejected'
this.reason = reason
// 执行所有失败回调
this.onRejected.forEach((fn) => fn())
}
}
// 立即执行传入的 executor 函数
// 如果执行过程中抛出错误,直接 reject
try {
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
// then 方法:支持链式调用
then(onFulfilled, onRejected) {
// 防止 onFulfilled / onRejected 不是函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (v) => v
onRejected =
typeof onRejected === 'function'
? onRejected
: (e) => {
throw e
}
// 返回新的 Promise,支持链式调用
return new MyPromise((resolve, reject) => {
// 如果当前是 fulfilled 状态,异步执行 onFulfilled
if (this.state === 'fullilled') {
setTimeout(() => {
try {
const x = onFulfilled(this.value) // 执行回调
resolve(x) // 把结果交给下一个 Promise
} catch (err) {
reject(err) // 出错交给下一个 Promise 的 reject
}
})
}
// 如果当前是 rejected 状态,异步执行 onRejected
if (this.state === 'rejected') {
setTimeout(() => {
try {
const x = onRejected(this.reason)
resolve(x)
} catch (err) {
reject(err)
}
})
}
// 如果当前是 pending 状态,需要把回调存起来(发布订阅)
if (this.state === 'pending') {
this.onFulfilled.push(() => {
setTimeout(() => {
try {
const x = onFulfilled(this.value)
resolve(x) // 把结果交给下一个 Promise
} catch (err) {
reject(err) // 出错交给下一个 Promise 的 reject
}
})
})
this.onRejected.push(() => {
setTimeout(() => {
try {
const x = onRejected(this.reason)
resolve(x)
} catch (err) {
reject(err)
}
})
})
}
})
}
}
// new MyPromise((resolve, reject) => {
// setTimeout(() => resolve('Hello mmx!'), 500)
// })
// .then((res) => {
// console.log('第一次 then:', res)
// return res + ' 🚀'
// })
// .then((res) => {
// console.log('第二次 then:', res)
// })
// =======promise封装===============
function myFetch(url, option = {}) {
return new Promise((resolve, reject) => {
fetch(url, option)
.then((response) => {
if (!response.ok) {
reject(new Error(`HTTP Error: ${response.status}`))
}
return response.json() // 自动解析json
})
.then((data) => resolve(data))
.catch((err) => reject(err))
})
}
// ===== 手写 Promise.all =====
function myPromiseAll(promises) {
return new Promise((resolve, reject) => {
// 1. 参数必须是数组,否则报错
if (!Array.isArray(promises)) {
return reject(new TypeError('Argument must be an array'))
}
const results = [] // 用来存放每个 Promise 的结果
let completedCount = 0 // 已完成的数量
// 如果传进来的是空数组,直接返回空结果
if (promises.length === 0) {
return resolve([])
}
// 2. 遍历数组中的每一个 Promise
promises.forEach((p, index) => {
// 用 Promise.resolve 包一层,保证即使 p 不是 Promise 也能被当成 Promise 处理
Promise.resolve(p).then(
(value) => {
// 成功时,保存结果到对应位置(保证顺序一致)
results[index] = value
completedCount++
// 如果所有 Promise 都完成了,就 resolve 整个数组
if (completedCount === promises.length) {
resolve(results)
}
},
(err) => {
// 只要有一个失败,就立刻 reject
reject(err)
}
)
})
})
}
Promise.allSettled
- 等 所有 Promise 都完成(无论是成功还是失败)。
- 最终返回一个数组,数组里每一项都描述了对应 Promise 的结果:
- 成功 →
{ status: 'fulfilled', value: xxx } - 失败 →
{ status: 'rejected', reason: xxx }
- 成功 →
function myPromiseAllSettled(promises) {
return new Promise((resolve, reject) => {
if (!Array.isArray(promises)) {
return reject(new TypeError('Argument must be an array'))
}
const results = []
let completedCount = 0
if (promises.length === 0) {
return resolve([])
}
promises.forEach((p, index) => {
Promise.resolve(p).then(
(value) => {
results[index] = { status: "fulfilled", value }
completedCount++
if (completedCount === promises.length) {
resolve(results)
}
},
(err) => {
results[index] = { status: "rejected", reason: err }
completedCount++
if (completedCount === promises.length) {
resolve(results)
}
}
)
})
})
}
css手写题:
场景
代码输出题