1. Promise.all
模拟实现Promise.all函数
javascript
function myPromiseAll(promises) {
// 检查是否是迭代对象
if(typeof promises[Symbol.iterator] !== 'function') {
throw(`传入的参数不是一个可迭代对象`)
}
return new Promise((resolve, reject) => {
const results = [];
let count = 0;
promises.forEach((promise, index) => {
Promise.resolve(promise)
.then(result => {
results[index] = result;
count++;
if (count === promises.length) {
resolve(results);
}
})
.catch(error => {
reject(error);
});
});
});
}
// test
const arr = [Promise.resolve(1),Promise.resolve(2),Promise.resolve(3)];
myPromiseAll(arr).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
2. 将虚拟DOM进行渲染
javascript
function render(vnode) {
const { tag, attrs = {}, children } = vnode;
//创建dom
let el = document.createElement(tag);
//添加attr
for (const key in attrs) {
el.setAttribute(key, attrs[key]);
}
//处理children
if (Array.isArray(children) && children.length > 0) {
children.forEach(child => el.appendChild(render(child)));//子元素添加在父元素中
}
return el
}
3. 树结构转数组结构
javascript
function treeToList(data) {
let result = []
data.forEach(item => {
result.push(item)
if (item.children) {
result = result.concat(treeToList(item.children))
delete item.children
}
})
return result
}
const data = [
{
id: '1',
name: '父节点1',
children: [
{
id: '1-1',
name: '子节点1-1',
children: [
{
id: '1-1-1',
name: '子节点1-1-1'
},
{
id: '1-1-2',
name: '子节点1-1-2'
}
]
}
]
},
{
id: '2',
name: '父节点2',
children: [
{
id: '2-1',
name: '子节点2-1'
}
]
}
]
console.log(treeToList(data))
4. 数组结构转树结构
javascript
// 示例数组
const dataArray = [
{ id: 1, name: 'Node 1' },
{ id: 2, name: 'Node 2', parentId: 1 },
{ id: 3, name: 'Node 3', parentId: 1 },
{ id: 4, name: 'Node 4', parentId: 2 },
{ id: 5, name: 'Node 5', parentId: 2 },
{ id: 6, name: 'Node 6' },
{ id: 7, name: 'Node 7', parentId: 6 },
];
// 将数组转换为树结构
function buildTree(dataArray, parentId = undefined) {
const tree = [];
for (const item of dataArray) {
if (item.parentId === parentId) {
const children = buildTree(dataArray, item.id);
if (children.length) {
item.children = children;
}
tree.push(item);
}
}
return tree;
}
const tree = buildTree(dataArray);
console.log(JSON.stringify(tree, null, 2));
5. 深拷贝
javascript
function isObject(val) {
return typeof val === "object" && val !== null;
}
// 难点如下:
// 1)注意收集 Symbol 属性
// 2) 注意可能会有循环引用,所以使用 map 记录,返回一个循环的指向即可,避免持续运行爆栈
// 3)注意一些特殊对象,Function,RegExp,Date,Map,Set这些,需要去构造新的实例
// 3)区分数组还是对象, 数组会有 ['0', '1', '2', 'length'] 这些属性,构造新的对象
function deepClone(obj, hash = new WeakMap()) {
if (!isObject(obj)) return obj; // 不是对象不拷贝直接返回
if (hash.has(obj)) { // 如果哈希表中有这个对象的记录,取出并返回
return hash.get(obj);
}
// 函数 正则 日期 ES6新对象,执行构造题,返回新的对象
const constructor = obj.constructor;
if (/^(Function|RegExp|Date|Map|Set)$/i.test(constructor.name)) return new constructor(obj);
let target = Array.isArray(obj) ? [] : {}; // 拷贝对象还是数组,创建新的内存地址
hash.set(obj, target); // hash 表记录对象
Reflect.ownKeys(obj).forEach((item) => {
if (isObject(obj[item])) { // 子属性(包括Symbol)也是对象的话递归调用
target[item] = deepClone(obj[item], hash);
} else {
target[item] = obj[item];
}
});
return target;
}
6. 实现事件触发器
javascript
class EventEmiter {
constructor() {
this.cache = {}; //存放不同的事件
}
on(name,fn){ //事件名,回调
if(this.cache[name]){
this.cache[name].push(fn)
}
else{
this.cache[name] = [fn]; //添加新事件
}
}
off(name,fn){ //删除事件的某个回调
let tasks = this.cache[name]; //拿到对应的回调队列
if(tasks){
const index = tasks.findIndex(f => f === fn);
if(index >= 0){
tasks.splice(index,1)
}
}
}
emit(name,once = false,...args){
if(this.cache[name]){
//创建副本,如果回调函数内继续注册相同事件会造成死循环
let tasks = this.cache[name].slice();
for(let fn of tasks){
fn(...args)
}
if(once){
delete this.cache[name]
}
}
}
}
//test
let eventsBus = new EventEmiter()
let fn1 = function(name,age){
console.log(name,age)
}
let fn2 = function(name,age){
console.log('fn',name,age);
}
eventsBus.on("test",fn1)
eventsBus.on("test",fn2)
eventsBus.emit("test",false,"Jason",18)
//Jason 18
//fn Jason 18
7. Promise.allsetted
javascript
Promise.myAllSettled = function (proms) {
return new Promise((resolve, reject) => {
let settledCount = 0; //状态已经确定的promise数
let count = 0; //promise总数
const result = [];
for (const prom of proms) {
let i = count;
count++;
Promise.resolve(prom).then(
(data) => {
settledCount++;
result[i] = {
status: "fullfilled",
value: data,
};
},
(reason) => {
settledCount++;
result[i] = {
status: "rejected",
reason,
};
}
).finally(() => {
if(settledCount >= count){
resolve(result)
}
});
}
});
};
const pro = new Promise((resolve, reject) => {
setTimeout(() => {
reject(3);
}, 1000);
});
Promise.allSettled([pro, Promise.resolve(1), Promise.reject(2)]).then(
(data) => {
console.log(data);
}
);
Promise.myAllSettled([pro, Promise.resolve(1), Promise.reject(2)]).then(
(data) => {
console.log(data);
}
);
8. Promise.race
javascript
Promise.myRace = function(arr){
return new Promise((resolve,reject) => {
for(let item of arr){
Promise.resolve(item).then(res => {
resolve(res)
}).catch(err => {
reject(err)
})
}
})
}
let p1 = new Promise(resolve => {
setTimeout(() => {
resolve(1)
},100)
})
let p2 = new Promise((resolve,reject) => {
setTimeout(() => {
reject(2)
})
})
let p3 = new Promise((resolve,reject) => {
setTimeout(() => {
resolve(3)
})
})
Promise.myRace([p1,p2,p3]).then(res => {
console.log(res);
}).catch(err => {
console.log(err);
})
9. 交通灯交替打印功能
javascript
function red() {
console.log('red')
}
function green() {
console.log('green')
}
function yellow() {
console.log('yellow')
}
const task = (timer, light) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (light === 'red') {
red()
} else if (light === 'green') {
green()
} else if (light === 'yellow') {
yellow()
}
resolve()
}, timer)
})
}
const taskRunner = async () => {
await task(3000, 'red')
await task(1000, 'green')
await task(2000, 'yellow')
taskRunner()
}
taskRunner()
10. 指定间隔时间重复执行一个函数n次
javascript
function one(func,wait,args){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
func.call(this,...args)
resolve()
},wait)
})
}
function repeat(func, times, wait) {
return async function(...args){
for (let i = 1; i <= times; i++) {
await one(func,wait,args)
}
}
}
const repeatLog = repeat(console.log,5,1000)
repeatLog("hello world")
11. 封装一个工具函数输入一个promiseA返回一个promiseB如果超过1s没返回则抛出异常如果正常则输出正确的值
javascript
function timeoutPromise(promise, timeout) {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
reject(new Error(`Promise timed out after ${timeout} ms`));
}, timeout);
promise
.then((result) => {
clearTimeout(timer);
resolve(result);
})
.catch((error) => {
clearTimeout(timer);
reject(error);
});
});
}
12. fetch请求5s之后取消请求
javascript
function abortFetch(url){
let res = null;
const abort = new AbortController();
fetch(url,{
signal: abort.signal
}).then(_res => res = _res);
setTimeout(() => {
if(!res){
abort.abort();
}
}, 5000)
}
13. 每个1S打印12345
javascript
for (let i = 1; i <= 5; i++){
setTimeout(()=>console.log(i),i*1000)
}
// 或者
function delay(i) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(i);
resolve();
}, 1000);
});
}
async function generate() {
for (let i = 1; i <= 5; i++) {
await delay(i);
}
}
generate();
14. setTimeout实现setInterval
javascript
function mySetInterval(func, delay, ...args) {
let timer = null
function fun() {
return setTimeout(() => {
func(...args)
timer = fun()
}, delay)
}
timer = fun()
return () => { clearTimeout(timer) }
}
let clear = mySetInterval(() => {
console.log(11);
}, 1000)
setTimeout(() => {
clear()
}, 2100)
15. setInterval实现setTimeout
javascript
function _setTimeout(fn, delay, ...args) {
const timer = setInterval(() => {
fn.apply(this, args);
clearInterval(timer);
},delay)
}
16. 请求超过4s自动失败
javascript
function getData () {
return Promise.race([
new Promise((resolve, reject) => {
// 模拟一个6s的请求
setTimeout(() => {
resolve('请求成功')
}, 6000)
}),
new Promise((resolve, reject) => {
setTimeout(() => {
reject('请求失败')
}, 4000)
})
]).then(res => {
console.log(res)
}, (error) => {
console.log(error)
})
}
17. 并发请求
javascript
/**
* 并发请求
*/
export function concurRequest(urls, maxNum){
return new Promise(resolve => {
let index = 0; // 下一次发送请求的下表
let count = 0; // 完成的请求数量
const result = []; // 最终的结果
async function request(){
const i = index;
const url = urls[i];
index++;
try {
const resp = await fetch(url);
result[i] = resp;
} catch (error) {
result[i] = error;
}finally{
count++;
if(count === urls.length){
resolve(result);
}
if(index < urls.length){
request();
}
}
}
for(let i = 0; i <Math.min(maxNum, urls.length); i++){
request();
}
})
}
18. 设计一个简单的任务队列, 要求分别在 1,3,4 秒后打印出 "1", "2", "3"
javascript
// 设计一个简单的任务队列, 要求分别在 1,3,4 秒后打印出 "1", "2", "3"
class TaskQueue {
constructor() {
this.queue = []
}
add(time, fn, ...args) {
let p = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(fn(...args))
}, time);
})
}
this.queue.push(p)
return this
}
async execute() {
let results = []
while(this.queue.length) {
await this.queue.shift()().then(res => results.push(res))
}
return results
}
}
let q =new TaskQueue()
console.log(q.add(1000, () => {
console.log(1)
return '1'
}).add(2000, () => {
console.log(2)
return '2'
}).add(1000, () => {
console.log(3)
return '3'
}).execute().then(res => console.log(res)));
19. 实现有并发限制的Promise调度器
JS 实现一个带并发限制的异步调度器 Scheduler,保证同时运行的任务最多有两个。 例如目前有 4 个任务,完成时间分别为,1000ms、500ms、300ms、400ms 那么在该调度器中的执行完成顺序应该为 2、3、1、4 分析:因为1、2先进入队列中,2完成则输出2,3进入,3完成输出3,此时为800ms,4进入后的200ms,1完成输出1,而后4完成输出
javascript
class Scheduler {
constructor(limit) {
this.limit = limit;
this.queue = [];
this.running = 0; // 当前执行任务的数量
}
createTask(duration, fn) {
return () => new Promise((resolve) => {
setTimeout(() => {
resolve(fn());
}, duration);
});
}
addTask(callback, duration) {
const task = this.createTask(duration, callback);
this.queue.push(task);
}
start() {
for (let i = 0; i < this.limit; i++) {
this.scheduler();
}
}
scheduler() {
if (this.queue.length === 0 || this.running > this.limit) {
return;
}
this.running++;
const task = this.queue.shift();
task().then((() => {
this.running--;
this.scheduler();
}));
}
}
// 实例化一个调度器
const scheduler = new Scheduler(2);
// 添加任务
scheduler.addTask(() => {
console.log("任务1");
}, 1000);
scheduler.addTask(() => {
console.log("任务2");
}, 500);
scheduler.addTask(() => {
console.log("任务3");
}, 300);
scheduler.addTask(() => {
console.log("任务4");
}, 400);
// 任务执行
scheduler.start();
20. 每个三秒输出时间
javascript
const task = (timer) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(new Date())
resolve()
}, timer);
})
}
const taskRunner = async () => {
await task(3000);
taskRunner();
}
taskRunner();
21. async_pool
javascript
async function asyncPool(poolLimit, iterable, iteratorFn) {
const ret = [];
const executing = new Set();
for (const item of iterable) {
const p = Promise.resolve().then(() => iteratorFn(item, iterable));
ret.push(p);
executing.add(p);
const clean = () => executing.delete(p);
p.then(clean).catch(clean);
if (executing.size >= poolLimit) {
await Promise.race(executing);
}
}
return Promise.all(ret);
}
module.exports = asyncPool;
22. Promise.retry
javascript
/**
* 超时重新请求,并在重试一定次数依然失败时输出缓存内容
* @param {*} promiseFactory 一个返回 Promise 的函数,表示要执行的异步操作。
* @param {*} maxRetries 一个整数,表示最大的重试次数。
* @param {*} timeout 一个整数,表示每次重试的间隔时间(单位为毫秒)。
* @param {*} cache 一个可选的参数,表示缓存的内容,如果重试多次依然失败,则会返回该缓存内容。
* @returns promise
*/
function retry(promiseFactory, maxRetries, timeout, cache=null) {
return new Promise((resolve, reject) => {
let retries = 0;
const executePromise = () => {
promiseFactory()
.then(resolve)
.catch((error) => {
retries++;
if (retries >= maxRetries) {
if (cache) {
resolve(cache);
} else {
reject(error);
}
} else {
setTimeout(executePromise, timeout);
}
});
};
executePromise();
});
}
// ----------test----------
!(() => {
const fetchData = () => {
// 返回一个 Promise 对象,表示异步请求数据
return fetch('http://example.com/data')
.then((response) => response.json())
.then((data) => {
// 处理数据
return data;
});
};
retry(fetchData, 3, 10000, '缓存内容')
.then((data) => {
// 成功获取数据
console.log(data);
})
.catch((error) => {
// 请求失败或超时,并且重试多次依然失败
console.error(error);
});
})()
23. 写一个 mySetInterVal(fn, a, b),每次间隔 a,a+b,a+2b 的时间,然后写一个 myClear,停止上面的 mySetInterVal
javascript
function mySetInterVal(fn, a, b){
let currentTimer = null;
let counter = 0;
function step(){
currentTimer = setTimeout(() => {
counter++;
fn();
step();
}, a + counter * b);
}
step();
return () => {
clearTimeout(currentTimer);
}
}
// test
const clear = mySetInterVal(() => console.log('runing'), 1000, 1000);
setTimeout(clear, 30000);
24. 产生不重复的随机数组
javascript
/**
* 产生一个不重复的随机数组
*/
function createRandomNum(min = 1, max = 100){
return min + Math.ceil(Math.random()*(max - min));
}
function noReaptRandomArr(){
const randomLength = createRandomNum(1, 30);
const arr = Array(randomLength).fill(createRandomNum());
const set = new Set(arr);
while(set.size < randomLength){
set.add(createRandomNum())
}
return [...set]
}
console.log(noReaptRandomArr())
25. 递归完成1-100累加
javascript
function recurcive100(num = 100){
if(num === 1){
return 1;
}
return recurcive100(num - 1) + num;
}
console.log(recurcive100())