前端面试编程题(异步调度,Promise实现、占用空间大小、渲染虚拟节点、实现for of)

目录

异步调度问题

题目一

答案

题目二

 答案

递归输出

题目一

 答案

Promise相关

题目一

答案

占用空间大小

题目一

答案

渲染虚拟节点

题目一

答案

实现for of

题目一

答案

 


异步调度问题

题目一

1.实现一个带并发限制的异步调度Scheduler,保证同时运行的任务最多有N个。完善下面代码中的Scheduler类,使以下程序能正确输出

class Scheduler {
	add(promiseCreator) {

	}
}
const timeout = (time) => new Promise(resolve => {
	setTimeout(resolve, time)
})
const scheduler = new Scheduler(3)
const addTask = (time, order) => {
	scheduler.add(() => timeout(time)).then(() => console.log(order))
}
addTask(1000, 'a')
addTask(500, 'b')
addTask(100, 'c')
addTask(2000, 'd')
addTask(10, 'f')

答案

方法一

class Schdule {
	constructor(n) {
		this.max = max
		this.count = 0
		this.queue = []
	}
	async add(promiseCreator) {
		if (this.count >= this.max) {
			await new Promise((resolve, reject) => this.queue.push(resolve))
		}
		this.count++;
		let res = await promiseCreator()
		this.count--;
		if (this.queue.length) {
			this.queue.shift()()
		}
		return res
	}
}

方法二 

class Schdule {
	constructor(n) {
		this.max = max
		this.count = 0
		this.queue = []
	}
	add(promiseCreator) {
		return new Promise(resolve => {
			promiseCreator.resolve = resolve; // 保存当前promise的状态
			if (this.count < this.max) { // 最大并发任务处理
				this.runWork(promiseCreator)
			} else {
				this.queue.push(promiseCreator)
			}
		})
	}
	runWork(promiseCreator) {
		this.count++
		promiseCreator().then(() => {
			promiseCreator.resolve()
			this.count--
			if (this.queue.length) {
				this.runWork(this.queue.shift())
			}
		})
	}
}

题目二

实现一个带并发限制的异步调度PromiseQueue,保证同时运行的任务最多有N个且按优先级运行任务。完善下面代码中的PromiseQueue类,使以下程序能正确输出

link1start
link2start
link3start
link2end
high prioritystart
link1end
link5start
link3end  
link4start
high priorityend
link4end
link5end

const urls = [
    {
        info: 'link1',
        time: 3000,
        priority: 1
    }, {
        info: 'link2',
        time: 2000,
        priority: 2
    }, {
        info: 'link3',
        time: 3000,
        priority: 3
    }, {
        info: 'link4',
        time: 2000,
        priority: 2
    }, {
        info: 'link5',
        time: 5000,
        priority: 5
    }
]
function loadImg(url) {
    return new Promise((resolve, reject) => {
        console.log(url.info + 'start')
        setTimeout(() => {
            console.log(url.info + 'end')
            resolve()
        }, url.time)
    })
}
class PromiseQueue {
    
}
const q = new PromiseQueue({
    concurrency: 3
})
const formatTask = (url) => {
    return {
        fn: () => loadImg(url),
        priority: url.priority
    }
}
urls.forEach(url => {
    q.add(formatTask(url))
})
const highPriorityTask = {
    priority: 10,
    info: 'high priority',
    time: 2000
}
q.add(formatTask(highPriorityTask))

 答案

class PromiseQueue {
    constructor(options = {}) {
        this.concurrency = options.concurrency || 1;
        this.currentCount = 0
        this.pendingList = []
    }
    add(task) {
        this.pendingList.push(task)
        this.run()
    }
    run() {
        if (this.pendingList.length === 0) {
            return;
        }
        if (this.concurrency === this.currentCount) {
            return;
        }
        this.currentCount++
        const { fn } = this.pendingList.sort((a, b) => b.priority - a.priority).shift()

        fn().then(this.completeOne.bind(this)).catch(this.completeOne.bind(this))
    }
    completeOne() {
        this.currentCount--
        this.run()
    }
}

递归输出

题目一

实现compose函数使下面的代码输出顺序为1,2,3,3.3,2.2,1.1 。

let middleware = []
middleware.push((next)=>{
	console.log(1)
	next()
	console.log(1.1)
})
middleware.push((next)=>{
	console.log(2)
	next()
	console.log(2.2)
})
middleware.push((next)=>{
	console.log(3)
	next()
	console.log(3.3)
})
let fn = compose(middleware)
fn()

这里观察输出1后执行next,然后输出2,执行next在到3,然后3.3,回到2.2,再回到1.1,这里明显为一个递归调用,传入的参数next为递归的函数,每次调用便会执行下一个函数。 

 答案

function compose(...args){
	let index = 0;
	return function fn() {
		if(args[0].length<=index){
			return;
		}
		return args[0][index++](fn)
	}
}
function compose(middlewares) {
	const copyMidlewares = [...middlewares];
	let index = 0
	const fn = () => {
		if (index >= copyMidlewares.length) {
			return ;
		}
		const middlewares = copyMidlewares[index];
		index++;
		return middlewares(fn)
	}
	return fn;
}

Promise相关

题目一

编写Promise.all和Promise.race方法。

答案

Promise.all = (arr) => {
	if (!Array.isArray(arr)) {
		return;
	}
	let counter = 0;
	let result = []
	return new Promise((resolve, reject) => {
		arr.forEach((item, index) => {
			Promise.resolve(item).then(value => {
				result[index] = value;
				counter++;
				if (counter === arr.length) {
					resolve(result);
				}
			}).catch(err => {
				reject(err)
			})
		})
	});
}
Promise.rRace = function (arr) {
	if (!Array.isArray(arr)) {
		return;
	}
	return new Promise((resolve, reject) => {
		arr.forEach(v => {
			Promise.resolve(v).then(value=>{
				resolve(value)
			}).catch(err => reject(err))
		})
	})
}

占用空间大小

题目一

1.计算一个变量占用的字节数(string占2字节,boolean占4字节,number占8字节)

注意

1.对象的属性值也占用字

2.相同的引用只能算一次

答案

const set = new Set()
function sizeOfObject(object) {
	if (object == null) {
		return 0
	}
	let bytes = 0;
	const properties = Object.keys(object)
	for (let i = 0; i < properties.length; i++) {
		const key = properties[i]
		if (typeof object[key] === 'object' && object[key] !== null) {
			if (set.has(object[key])) {
				bytes += calculator(key)
				continue
			}
			set.add(object[key])
		}
		bytes += calculator(key)
		bytes += calculator(object[key])
	}
	return bytes
}
function calculator(object) {
	switch (typeof object) {
		case 'string': {
			return object.length * 2
		}
		case 'boolean': {
			return 4
		}
		case 'number': {
			return 8
		}
		case 'object': {
			if (Array.isArray(object)) {
				return object.map(calculator).reduce((res, current) => res + current, 0)
			}
			return sizeOfObject(object)
		}
	}
}

渲染虚拟节点

题目一

1.将如下数据格式的虚拟dom为真实dom

const vnode = {
    tag: 'DIV',
    attrs: {
        id: 'app'
    },
    children: [
        {
            tag: 'SPAN',
            children: [{
                tag: 'A',
                children: []
            }]
        },
        {
            tag:'SPAN',
            children:[
                {
                    tag:'A',
                    children:[]
                },
                {
                    tag:'',
                    children:[]
                }
            ]
        }
    ]
}

答案

function render(vnode){
    if(typeof vnode === 'number'){
        vnode = String(vnode)
    }
    if(typeof vnode=== 'string'){
        return document.createTextNode(vnode);
    }
    const  element = document.createElement(vnode.tag);
    if(vnode.attrs){
        Object.keys(vnode.attrs).forEach(key=>{
            element.setAttribute(key,vnode.attrs[key])
        })
    }
    if(vnode.children.length){
        vnode.children.forEach(v=>{
            element.appendChild(render(v))
        })
    }
    return element
}

实现for of

题目一

如何使对象实现for of。

答案

方法一

obj[Symbol.iterator]=function(){
    const _this = this
    //也可使用: keys = Object.getOwnPropertyNames(this)
    const keys = Object.keys(this)
    let index = 0
	return {
		next(){
        	return {
	        	value: _this[keys[index++]],
	        	done: index>keys.length
        	}
        }
    }
}

方法二

obj[Symbol.iterator] = function *(){
    for(let prop in this){
        yield this[prop]
    }
}

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值