先贴图:
带大家一步一步完成这个代码,第一步就是有个异步类,该类包括调度的名称、调度的执行函数、同时执行的任务个数、当前正在执行的个数、剩余任务队列:
const quene = new asyncQuene({
name: 'addNumber',
process,
limit: 2
})
constructor(options) {
// 调度的名称
this.name = options.name
// 调度的执行函数
this.process = options.process
// 同时执行的任务个数
this.limit = options.limit
// 用来判断是否同一个任务
this.entries = new Map()
// 剩余任务队列
this.quened = []
// 当前正在执行的个数
this.activeTasks = 0
// 是否开始下一次循环
this.startNextLoop = true
}
然后该调度还能添加任务:
quene.add({
key: 1
}, (err, res) => {
console.log('1的处理结果');
})
quene.add({
key: 2
}, (err, res) => {
console.log('2的处理结果');
})
quene.add({
key: 1
}, (err, res) => {
console.log('1的处理结果' );
})
quene.add({
key: 3
}, (err, res) => {
console.log('3的处理结果');
})
添加的任务第一个是对象,第二个是执行完成的回调。
可以得到类有个添加的方法:
- 添加任务
add(item, callback) {
const newEntry = {
item,
callback,
state: 0,
result: null,
error: null,
callbacks:null
}
const key = item.key
const entry = this.entries.get(key)
if (entry) {
// 前面相同的已经执行完毕
if (entry.state === 2) {
callback(entry.error, entry.result)
} else if (!entry.callbacks) {
// 如果没有callbacks
entry.callbacks = [callback]
} else {
// 前面的还没执行完毕
entry.callbacks.push(callback)
}
return;
}
this.entries.set(item.key, newEntry)
// 入栈
this.quened.push(newEntry)
// 需要开启下一个回调
if (this.startNextLoop) {
// 1.这里异步是要先添加所有任务再开始处理 2.startNextLoop作用是控制不会每个任务都立马执行
Promise.resolve().then(() => {
this.startTasks()
})
this.startNextLoop = false
}
}
- 开始执行任务
startTasks() {
// 会一个个处理队列对象,直到limit
while (this.activeTasks < this.limit) {
// 获取任务
const target = this.quened.shift()
if (!target) break
// 当前执行数加一
this.activeTasks++
target.state = 1
// 执行处理函数,并将处理结果回调并进入handleRes
this.process(target.item, (err, res) => {
this.handleRes(target, err, res)
})
}
// 处理完这一轮才能开始下一轮
this.startNextLoop = true
}
- 处理回调结果
handleRes(entry, err, res) {
const callback = entry.callback
const callbacks = entry.callbacks
entry.state = 2
entry.result = res
entry.error = err
// 当前进行的任务减一
this.activeTasks--
// 执行任务执行完毕的回调
callback(err, res)
// 如果有同样的key值的会放入callbacks,此时就可以执行所有的回调
if (callbacks) {
for (const callback of callbacks) {
callback(err, res);
}
}
// 如果startNextLoop为true表示要开始下一波循环,每次执行完一个任务都要判断
// 是否要进入下一波循环
if (this.startNextLoop) {
Promise.resolve().then(() => {
this.startTasks()
})
this.startNextLoop = false
}
}
const process = (item, callback) => {
setTimeout(function() {
callback(null, item)
}, 1000)
}
完整代码如下:
class asyncQuene {
constructor(options) {
this.name = options.name
this.process = options.process
this.limit = options.limit
this.entries = new Map()
this.quened = []
this.activeTasks = 0
this.startNextLoop = true
}
add(item, callback) {
const newEntry = {
item,
callback,
state: 0,
result: null,
error: null,
callbacks:null
}
const key = item.key
const entry = this.entries.get(key)
if (entry) {
if (entry.state === 2) {
callback(entry.error, entry.result)
} else if (!entry.callbacks) {
entry.callbacks = [callback]
} else {
entry.callbacks.push(callback)
}
return;
}
this.entries.set(item.key, newEntry)
this.quened.push(newEntry)
this.startLoop()
}
startLoop() {
// 处理完这一轮,开始下一轮
if (this.startNextLoop) {
Promise.resolve().then(() => {
this.startTasks()
})
this.startNextLoop = false
}
}
startTasks() {
while (this.activeTasks < this.limit) {
const target = this.quened.shift()
if (!target) break
this.activeTasks++
target.state = 1
this.process(target.item, (err, res) => {
this.handleRes(target, err, res)
})
}
this.startNextLoop = true
}
handleRes(entry, err, res) {
const callback = entry.callback
const callbacks = entry.callbacks
entry.state = 2
entry.result = res
entry.error = err
this.activeTasks--
callback(err, res)
if (callbacks) {
for (const callback of callbacks) {
callback(err, res);
}
}
this.startLoop()
}
}
const quene = new asyncQuene({
name: 'addNumber',
process,
limit: 2
})
quene.add({
key: 1
}, (err, res) => {
console.log('1的处理结果' + res.number);
})
quene.add({
key: 2
}, (err, res) => {
console.log('2的处理结果');
})
quene.add({
key: 1
}, (err, res) => {
console.log('1的处理结果' + res.number);
})
quene.add({
key: 3
}, (err, res) => {
console.log('3的处理结果');
})
给大家几个问题:
- add方法最后不用微任务会怎样?
不用微任务那么如果有多次add,那么只会执行一次。因为第一个add添加完就开始执行了,startNextLoop已经是false,此时会再次执行add方法,但是startNextLoop为false就不会进入startTasks,所以不用微任务就只会执行一次