以下是我自己的写法,并不保证是标准的写法,答案仅做参考
Promise基础
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class MyPromise {
constructor(executor) {
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
this.onFulfilledCallbacks.map(cb => cb());
}
};
const reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
this.onRejectedCallbacks.map(cb => cb());
}
};
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (v) => v;
onRejected = typeof onRejected === "function" ? onRejected: (e) => { throw e;};
const resolvePromise = (result, resolve, reject) => {
if (result instanceof MyPromise) {
result.then(resolve, reject);//透传状态
} else {
resolve(result); //转换成fulfilled
}
};
//这里可以看出来 then方法本质上是返回一个Promise对象实例
const newPromise = new MyPromise((resolve, reject) => {
const fulfilledFunc = () =>
setTimeout(() => {
//这里每个fulfilledFunc/rejectedFunc都是异步微任务,保证所有的顺序都保持一致
//而不是因为有微任务有同步任务而打乱最后的执行顺序
try {
const result = onFulfilled(this.value);
resolvePromise(result, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
const rejectedFunc = () => {
setTimeout(() => {
try {
const result = onRejected(this.reason);
resolvePromise(result, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
};
if (this.status === PENDING) {
this.onFulfilledCallbacks.push(fulfilledFunc);
this.onRejectedCallbacks.push(rejectedFunc);
} else if (this.status === FULFILLED) {
fulfilledFunc();
} else {
rejectedFunc();
}
});
return newPromise; //链式调用的本质
}
catch(onRejected) {
return this.then(null,onRejected)
}
}
Promise 链式调用的核心本质是:then/catch 方法每次调用都会返回一个全新的 Promise 实例,且新 Promise 的状态由上一个 then/catch 回调的返回值决定;这里每个fulfilledFunc/rejectedFunc都是异步微任务,保证所有的顺序都保持一致,而不是因为有微任务有同步任务而打乱最后的执行顺序
Promise.all
实现Promise.all方法:
- 接收一个可迭代对象(如数组),返回一个新 Promise;
- 所有 Promise 都成功时,返回结果数组(顺序和输入一致);
- 任意一个 Promise 失败时,立即返回该失败原因;
- 若输入为空数组,立即 resolve 空数组。
const myPromiseAll = (arrFunc) => {
return new Promise((resolve, reject) => {
let resArr = [];
let len = arrFunc.length;
let k = 0;
arrFunc.map((item, index) => {
Promise.resolve(item).then(
(res) => {
k++;
resArr[index] = res;
if (k === len) {
resolve(resArr);
}
},
(rej) => {
reject(rej);
}
);
});
});
};
const p1 = Promise.resolve(1);
const p2 = new Promise((resolve) => setTimeout(() => resolve(2), 1000));
const p3 = 3; // 非Promise值
myPromiseAll([p1, p2, p3])
.then((res) => {
console.log("all res:", JSON.stringify(res)); // [1,2,3]
})
.catch((err) => {
console.log("all err:", err);
});
Promise.race
实现Promise.race方法:
- 接收可迭代对象,返回新 Promise;
- 第一个完成(成功 / 失败)的 Promise 的结果 / 原因,就是最终结果;
- 若输入为空数组,永远 pending
const MyPromiseRace = (arrFunc) => {
return new Promise((resolve,reject) => {
arrFunc.map(item => {
Promise.resolve(item).then(res=>{
resolve(res)
},rej=>{
reject(rej)
})
});
});
};
const p1 = Promise.resolve(1);
const p2 = new Promise((resolve) => setTimeout(() => resolve(2), 1000));
const p3 = 3; // 非Promise值
const p4 = Promise.reject(new Error('fail'));
MyPromiseRace([p1, p4]).catch(err => {
console.log('all err:', err.message); // fail
});
Promise.allSettled
实现Promise.allSettled方法:
- 接收可迭代对象,返回新 Promise;
- 所有 Promise 都完成(成功 / 失败)后,返回结果数组;
- 每个结果对象格式:
{ status: 'fulfilled', value: xxx }或{ status: 'rejected', reason: xxx }; - 空数组立即 resolve 空数组
const MyPromiseAllSettled = (arrFunc)=>{
return new Promise((resolve,reject)=>{
let len = arrFunc.length
let resarr = []
let k = 0
arrFunc.map((item,index)=>{
Promise.resolve(item).then(res=>{
k++
resarr[index] = res
if(len === k){
resolve(resarr)
}
},rej=>{
k++
resarr[index] = rej
if(len === k){
resolve(resarr)
}
})
})
})
}
const p1 = Promise.resolve(1);
const p2 = new Promise((resolve) => setTimeout(() => resolve(2), 1000));
const p3 = 3; // 非Promise值
const p4 = Promise.reject(new Error('fail'));
MyPromiseAllSettled([p1,p2,p3, p4]).then(res => {
console.log('all err:', res); // fail
});
实现异步函数串行执行(基于 Promise)
给定一个异步函数数组(每个函数返回 Promise),实现一个函数,让这些函数串行执行(前一个完成后,再执行后一个),最终返回所有结果的数组。
这题豆包给出的答案是用arr.reduce,但是前一个完成后,再执行后一个第一时间我想到是async和await,不过async和await只能用for of或者for in遍历,不能使用arr.map/ arr.forEach完成,不然实现不了await的串行效果
const AsyncPromiseArr = (arrFunc) => {
return new Promise(async(resolve, reject) => {
const stack = [];
let i = 0
for(let func of arrFunc){
const res = await func();
stack.push(res);
i++
if(i === arrFunc.length){
resolve(stack)
}
}
});
};
// 测试用例
// 模拟异步函数
const task1 = () => new Promise(resolve => setTimeout(() => resolve(1), 500));
const task2 = () => new Promise(resolve => setTimeout(() => resolve(3), 1500));
const task3 = () => new Promise(resolve => setTimeout(() => resolve(2), 100));
AsyncPromiseArr([task1, task2, task3]).then(res => {
console.log('serial res:', res); // [1,2,3](执行顺序:task1→task2→task3)
});
实现 Promise 超时封装
实现一个函数withTimeout(promise, timeout, errMsg):
- 给 Promise 添加超时控制,若超过
timeout毫秒未完成,返回 reject(错误信息为errMsg); - 若 Promise 在超时前完成,正常返回结果;
- 超时后终止等待(无需取消原 Promise,仅控制返回结果)。
const withTimeout = (promise,timeout,errMsg)=>{
const promise1 = new Promise((resolve,reject)=>{
setTimeout(()=>reject(new Error(errMsg)),timeout)
})
return Promise.race([promise,promise1])
}
const p1 = new Promise(resolve => setTimeout(() => resolve(100), 500));
withTimeout(p1, 1000, '超时了').then(res => {
console.log('timeout res:', res); // 100
});
// 超时场景
const p2 = new Promise(resolve => setTimeout(() => resolve(200), 1500));
withTimeout(p2, 1000, '超时了').catch(err => {
console.log('timeout err:', err.message); // 超时了
});
实现一个防抖函数
触发事件后,第一次触发立即执行,后续延迟 n 毫秒执行回调函数;如果在这 n 毫秒内再次触发事件,重置延迟时间,同时增加cancel方法支持手动取消
const debounce_ = (fn,delay)=>{
let fn_
const debounceFunc = function(...args){
if(fn_){
clearTimeout(fn_)
}else{
fn.call(this,...args)
}
fn_ = setTimeout(()=>{
fn.call(this,...args)
fn_ = null
},delay)
}
debounceFunc.cancel = function(){
clearImmediate(fn_)
fn_ = null
}
return debounceFunc
}
function handleSearch(val) {
console.log('搜索:', val);
}
const debouncedSearchImmediate = debounce_(handleSearch, 1000);
debouncedSearchImmediate('a'); // 立即输出「搜索:a」
debouncedSearchImmediate('ab'); // 500ms内触发,重置定时器(无输出)
setTimeout(() => {
debouncedSearchImmediate('abc'); // 500ms后触发,立即输出「搜索:abc」
},2000);
// 测试取消功能
const debouncedCancel = debounce_(handleSearch, 500);
debouncedCancel('test');
debouncedCancel.cancel();
实现节流函数
触发事件后,每隔 n 毫秒只能执行一次回调函数,无论期间触发多少次,都会按固定频率执行
const throttle_ = (fn, delay) => {
let timer;
return function (...args) {
if (!timer) {
timer = setTimeout(() => {
fn.call(this, ...args);
timer = null
}, delay);
}
};
};
实现带 Promise 的防抖函数
实现一个防抖函数debounceWithPromise(fn, delay):
- 触发后延迟
delay毫秒执行函数; - 重复触发时重置延迟;
- 函数执行结果通过 Promise 返回;
- 支持取消防抖(添加
cancel方法)。
const debounceWithPromise = (fn, delay) => {
let timer;
let rejectFn;
const debounce_ = function (...args) {
if (timer) {
clearTimeout(timer);
rejectFn && rejectFn(new Error("Debounce reject"));
}
return new Promise((resolve, reject) => {
rejectFn = reject
timer = setTimeout(() => {
try {
const res = fn.call(this, ...args);
Promise.resolve(res).then(resolve, reject);
} catch (e) {
reject(e);
} finally {
rejectFn = null;
timer = null;
}
}, delay);
});
};
debounce_.cancel = function () {
if (timer) {
clearTimeout(timer);
timer = null;
rejectFn && rejectFn(new Error("Debounce reject"));
rejectFn = null;
}
};
return debounce_;
};
const fn = (num) =>
new Promise((resolve) => setTimeout(() => resolve(num * 2), 100));
const debouncedFn = debounceWithPromise(fn, 500);
// 重复触发
debouncedFn(1).catch((err) => console.log("err1:", err.message)); // Debounce reset
debouncedFn(2).then((res) => console.log("debounce res1:", res));
setTimeout(() => {
debouncedFn(3).then((res) => console.log("debounce res2:", res));
}, 600);
基于 Promise.race 实现 “超时重试” 函数
实现一个raceWithRetry函数,满足:
- 接收 4 个参数:
asyncTask(异步任务,返回 Promise)、timeout(超时时间,ms)、maxRetry(最大重试次数)、retryDelay(重试间隔,ms); - 执行
asyncTask,若超时 / 失败则自动重试,直到成功或重试次数耗尽; - 每次重试前等待
retryDelay毫秒; - 最终返回成功的结果,或重试耗尽后抛出错误。
const raceWithRetry = (asyncTask, timeout, maxRetry, retryDelay) => {
let cnt = maxRetry;
return new Promise((resolve, reject) => {
let promise1 = () =>
new Promise((_, reject_) =>
setTimeout(() => reject_(new Error("超时啦")), timeout)
);
const withRetry = () => {
Promise.race([asyncTask(), promise1()]).then(
(res) => {
resolve(res);
},
(rej) => {
if (cnt) {
cnt--;
setTimeout(() => withRetry(), retryDelay);
} else {
reject(rej);
}
}
);
};
withRetry();
});
};
// 模拟异步任务:随机成功/失败(测试重试)
function mockApi() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const random = Math.random();
if (random > 0.7) {
// 30%概率成功
resolve("API request success");
} else {
reject(new Error("API request failed"));
}
}, 100); // 任务执行时间300ms
});
}
// 测试1:超时重试(任务执行时间300ms,超时200ms,最大重试2次)
raceWithRetry(mockApi, 200, 2, 500)
.then((res) => console.log("retry1:", res)) // 重试1-2次后成功,输出结果
.catch((err) => console.log("retry1 err:", err.message)); // 若重试耗尽,输出错误
多接口竞速 + 超时 + 重试
实现一个fastestApiWithRetry函数,满足:
- 接收多个接口请求函数(数组)、单次超时时间、最大重试次数;
- 并行执行所有接口请求,用
Promise.race取最快的成功结果; - 若所有接口都超时 / 失败,则整体重试(最多
maxRetry次); - 最终返回最快的有效结果,或重试耗尽后抛出错误;
- 要求:即使某一个接口失败,不影响其他接口的竞速(仅忽略失败的接口)。