场景应用
1. 循环打印红黄绿
红灯 3s 亮一次,绿灯 1s 亮一次,黄灯 2s 亮一次;如何让三个灯不断交替重复亮灯?
function red() {
console.log('red');
}
function green() {
console.log('green');
}
function yellow() {
console.log('yellow');
}
// callback 实现
function task(timer, light, callback){
setTimeout(()=>{
if(light==='red') red()
if(light==='green') green()
if(light==='yellow') yellow()
callback()
}, timer)
}
function step(){
task(3000, 'red', ()=>{
task(1000, 'green', ()=>{
task(2000, 'yellow', step)
})
})
}
step()
// promise 实现
function task(timer, fn){
return new Promise((resolve, reject) => {
setTimeout(()=>{
fn()
resolve()
}, timer)
})
}
function step(){
task(3000, red)
.then(()=> task(1000, green))
.then(()=> task(2000, yellow))
.then(step)
}
step()
// async/await 实现
async function step(){
await task(3000, red)
await task(1000, green)
await task(2000, yellow)
step()
}
step()
2. 每隔一秒打印 1,2,3,4
// let
function printNum(){
for(let i=1; i<5; i++){
setTimeout(() => console.log(i), i*1000)
}
}
// 闭包
function printNum(){
for(var i=1; i<5; i++){
(function(num){
setTimeout(()=> console.log(num), num*1000)
})(i)
}
}
3. 小孩报数问题
有30个小孩儿,编号从1-30,围成一圈依此报数,1、2、3 数到 3 的小孩儿退出这个圈, 然后下一个小孩 重新报数 1、2、3,问最后剩下的那个小孩儿的编号是多少?
function childNum(num, count){
let allPlayer = []
for(let i=0; i<num; i++){
allPlayer[i] = i+1
}
let exitCount = 0
let curCount = 0
let curIndex = 0
while(exitCount < num -1){
if(allPlayer[curIndex] !==0) curCount++
if(curCount===count){
allPlayer[curIndex] = 0
curCount = 0
exitCount++
}
curIndex++
if(curIndex===num){
curIndex = 0
}
}
for(let i=0; i<num; i++){
if(allPlayer[i] !== 0) {
return allPlayer[i]
}
}
}
4. 用Promise实现图片的异步加载
function imgAsync(url){
return new Promise((resolve, reject)=>{
let img = new Image()
img.src = url
img.onload = () =>{
resolve()
}
img.onerror = () =>{
reject()
}
})
}
imgAsync(url).then(()=>{
console.log('加载成功!')
}).catch(()=>{
console.error('加载成功!')
})
5. 发布-订阅模式
class EventCenter{
let handlers = {}
addEventListener(type, handler){
if(!this.handlers[type]){
this.handlers[type] = []
}
this.handlers[type].push(handler)
}
dispatchEvent(type, params){
if(!this.handlers[type]){
console.error('no type')
return
}
this.handlers[type].forEach((fn)=>{
fn(...params)
})
}
removeEvent(type, handler){
if(!this.handlers[type]){
console.error('no type')
return
}
if(!handler){
delete this.handlers[type]
} else {
const index = this.handlers[type].indexOf(handler)
if(index>-1){
this.handlers[type].splice(index, 1)
if(this.handlers[type].length===0) {
delete this.handlers[type]
}
}else{
console.error('no handler')
return
}
}
}
}
6. 查找文章中出现频率最高的单词
function getMostWord(text){
if(!text) return
text = text.trim().toLowerCase()
text = text.match(/[a-z]+/g)
let map = {}
let maxItem = {maxWord: '', maxNum: 0}
text.forEach((item)=>{
let count = 1
if(Object.prototype.hasOwnProperty.call(map, item)){
count = map[item] + 1
}
map[item] = count
if(count>maxItem.maxNum){
maxItem = {maxWord: item, maxNum: count}
}
})
return maxItem
}
7. 版本号排序的方法
题目描述:有一组版本号如下 ['0.1.1', '2.3.3', '0.302.1', '4.2', '4.3.5', '4.3.4.5']。现在需要对其进行排序,排序的结果为 ['4.3.5','4.3.4.5','2.3.3','0.302.1','0.1.1']
function sortVersion(arr){
arr.sort((a, b)=>{
const arr1 = a.split(".")
const arr2 = b.split(".")
let i = 0
while(true){
const c1 = arr1[i]
const c2 = arr2[i]
i++
if(c1===undefined || c2===undefined){
return arr2.length - arr1.length
}
if(c1===c3) continue
return c2-c1
}
})
}
8. prototype继承
function Super(flag1){
this.flag1 = flag1
}
function Sub(flag2){
this.flag2 = flag2
}
const superInstance = new Super(true)
Sub.prototype = superInstance
const subInstance = new Sub(false)
subInstance.flag1 // true
subInstance.flag2 // false
9. 双向数据绑定
let obj = {}
const input = document.getElementById('input')
const span = document.getElementById('span')
Object.defineProperty(obj, 'text', {
configurable: true,
enumerable: true,
get(){
console.log('获取数据了')
}
set(newVal){
console.log('设置数据')
input.value = newVal
span.innerHTML = newVal
}
})
input.addEventListener('keyup', (e)=>{
obj.text = e.target.value
})
10. 简单路由
class Route{
constructor(){
this.routes = {}
this.currentHash = ''
this.freshRoute = this.freshRoute.bind(this)
window.addEventListener('load', this.freshRoute, false)
window.addEventListener('hashchange', this.freshRoute, false)
}
storeRoute(path, cb){
this.route[path] = cb || function (){}
}
freshRoute(){
this.currentHash = location.hash.slice(1) || '/'
this.route[this.currentHash]()
}
}
11. 斐波那契数列
// 递归
function fibonacci(n){
if(n===0) return 0
if(n===1) return 1
return fibonacci(n-2) + fibonacci(n-1)
}
// 动态规划
function fibonacci(n){
const arr = [1, 1, 2]
const len = arr.length
if(n < len){
return arr[n]
}
for(let i=len; i<n; i++){
arr.push(arr[i-2]+arr[i-1])
}
return arr[arr.length-1]
}
12. 字符串出现的不重复最长长度
function strLongLength(s){
let str = ''
let max = 0
for(let i=0; i<s.length; i++){
const item = s[i]
const index = str.indexOf(item)
if(index>-1){
max = Math.max(max, str.length)
str = str.slice(index+1) + item
}else{
str += item
}
}
return Math.max(max, str.length)
}
13. 使用 setTimeout 实现 setInterval
function mySetInterval(fn, timeout){
const timer = {
flag: true
}
function interval(){
if(timer.flag){
fn()
setTimeout(interval, timeout)
}
}
setTimeout(interval, timeout)
return timer
}
14. 判断对象是否存在循环引用
function isCircular(obj){
let visited = new Set()
const stack = [obj]
while(stack.length>0){
const current = stack.pop()
visited.add(current)
for(let key in current){
const value = current[key]
if(typeof value === 'object' && value !== null){
if(visited.has(value)) return true
}
stack.push(value)
}
}
return false
}
// 递归
function isCircular(obj, visited = new Set()){
visited.add(obj)
for(const key in obj){
const value = current[key]
if(typeof value === 'object' && value !== null){
if(visited.has(value) || isCircular(value, visited)) {
return true
}
}
}
return false
}
15. 高并发控制
// Promise模拟接口异步请求
function fetch(url) {
return new Promise(resolve => {
const random = Math.random()
setTimeout(() => {
console.log(url);
resolve('data:' + url);
}, random * 10000);
});
}
function multiRequest(urls, max) {
const len = urls.length;
const result = new Array(len).fill(false);
let count = 0;
return new Promise(resolve => {
// 首次并发 max 个请求
while (count < max) {
next();
}
function next() {
const cur = count++;
if (cur > len) {
!result.includes(false) && resolve(result);
return;
}
fetch(urls[cur]).then(res => {
result[cur] = res;
next();
});
}
});
}
multiRequest([
'api/1',
'api/2',
'api/3',
'api/4',
'api/5',
'api/6',
'api/7',
'api/8',
'api/9'
], 3).then(result => {
console.log('所有请求处理完毕');
console.log(result);
});
16. 实现一个函数,要求能在页面请求很多时候,尽可能的按照顺序的输出返回结果
function processRequest(urls){
let limit = 3
let result = []
let queue = urls.slice(0)
async function sendRequest(){
if(queue.length===0){
return result
}
let url = queue.shift()
try{
let res = await fetch(url)
res.push(res)
sendRequest()
}catch(err){
sendRequest()
}
}
for(let i=0; i<limit; i++){
sendRequest()
}
}
17. 自动重试 3 次,任意一次成功就直接返回
function fetchWithRetry(url, max=3){
return new promise((resolve, reject)=>{
async function doFetch(count){
try{
let res = await fetch(url)
if(res.code===200){
resolve(res)
}else{
throw new Error()
}
}catch(err){
if(count<max){
doFetch(count++)
}else{
reject(err)
}
}
}
doFetch(0)
})
}
18. 高并发控制 一次最多输出 n 个结果
function creator(count) {
const arr = [];
let flag = true;
function setup(fn) {
arr.push(fn);
// 包一层异步,让同步调用全部进来
flag && setTimeout(() => {
// 并发执行容器,初始化长度为 count
const promises = arr.splice(0, count).map((item, index) => {
// 返回下标,用于执行容器的替换
return item().then(() => index);
})
// 真正执行过程开始,此时 arr 中为第一轮排不上的异步任务
arr.reduce((pres, curFn) => {
return pres.then(() => {
return Promise.race(promises); // 返回先完成的下标
}).then(fastestIndex => {
// 要继续将这个下标返回,以便下一次遍历
promises[fastestIndex] = curFn().then(() => fastestIndex);
})
}, Promise.resolve());
}, 0) && (flag = false);// flag 控制异步只调用一次
}
return setup
}
const setup = creator(2)
setup(fn)
setup(fn)
setup(fn)
setup(fn)
setup(fn)
function fn() {
return new Promise((resolve) => {
setTimeout(() => {
console.log(Date())
resolve(1)
}, 2000)
})
}
19. 字节高并发面试题
// 分批请求,一次并发 limit 个,完了再并发下一批
async function fetchWithLimit(fetchList, limit) {
const results = [];
let completedCount = 0;
let errorOccurred = false;
async function fetchOne(fetchItem) {
try {
const result = await fetchItem();
console.log('Request succeeded:', result);
results.push(result);
} catch (error) {
console.error('Request failed:', error);
errorOccurred = true;
} finally {
completedCount++;
}
}
// 按并发限制逐个处理请求
while (completedCount < fetchList.length && !errorOccurred) {
const currentBatch = fetchList.slice(completedCount, completedCount + limit);
await Promise.all(currentBatch.map(fetchOne));
}
// 如果有请求失败,则抛出错误
if (errorOccurred) {
throw new Error('One or more requests failed');
}
return results;
}
// 限制并发,请求完一个进入下一个
async function fetchWithLimit(fetchList, limit) {
let results = [];
let currentIndex = 0;
let runningCount = 0;
let errorOccurred = false;
async function fetchAndHandle(index) {
try {
const result = await fetchList[index]();
results.push(result);
} catch (error) {
errorOccurred = true;
throw error;
} finally {
runningCount--;
if (!errorOccurred && currentIndex < fetchList.length) {
fetchAndHandle(currentIndex);
runningCount++;
currentIndex++;
}
}
}
while (currentIndex < fetchList.length && runningCount < limit) {
fetchAndHandle(currentIndex);
runningCount++;
currentIndex++;
}
while (runningCount > 0) {
await new Promise(resolve => setTimeout(resolve, 0)); // 等待当前请求完成
}
if (errorOccurred) {
throw new Error('One or more requests failed.');
}
return results;
}
// 示例的异步请求方法
function asyncFetch(url) {
return new Promise((resolve, reject) => {
const random = Math.random();
setTimeout(() => {
if (random < 1) {
console.log(url)
resolve(`Request to ${url} succeeded: ${random}`);
} else {
reject(`Request to ${url} failed: ${random}`);
}
}, random * 10000);
});
}
// 示例 fetchList
const fetchList = [
async () => asyncFetch('http://example.com/1'),
async () => asyncFetch('http://example.com/2'),
async () => asyncFetch('http://example.com/3'),
async () => asyncFetch('http://example.com/4'),
async () => asyncFetch('http://example.com/5'),
async () => asyncFetch('http://example.com/6'),
async () => asyncFetch('http://example.com/7'),
async () => asyncFetch('http://example.com/8'),
async () => asyncFetch('http://example.com/9'),
async () => asyncFetch('http://example.com/10'),
async () => asyncFetch('http://example.com/11')
];
const limit = 2;
(async () => {
try {
const result = await fetchWithLimit(fetchList, limit);
console.log('All requests succeeded:', result);
} catch (error) {
console.error('Request failed:', error.message);
}
})();
// 限制并发请求
function gets(ids, max) {
return new Promise((resolve) => {
const res = [];
let completedCount = 0;
function load(id, index) {
get(id)
.then((data) => {
res[index] = data;
})
.catch((err) => {
res[index] = err;
})
.finally(() => {
completedCount++;
if (completedCount === ids.length) {
resolve(res);
} else if (index < ids.length - 1) {
load(ids[index + max], index + max);
}
});
}
for (let i = 0; i < max && i < ids.length; i++) {
load(ids[i], i);
}
});
}
// 优雅的 RXJS 实现
function gets(ids, max) {
return from(ids).pipe(
mergeMap((id) => get(id), max), // 使用mergeMap一次性处理max个请求
toArray(), // 将结果转换为数组
map((results) => {
const resultMap = new Map();
results.forEach((result, index) => {
resultMap.set(ids[index], result); // 将结果映射到id上
});
return ids.map((id) => resultMap.get(id)); // 根据ids顺序返回结果数组
})
);
}