前言
假设我们有一个函数 arrange
,它可以按我们想要的顺序执行一些操作。比如:
function arrange(name: string) {}
arrange("William").execute();
// > William is notified
arrange("William").do("commit").execute();
// > William is notified
// > Start to commit
arrange("William").wait(5).do("commit").execute();
// > William is notified
// 等待5秒
// > Start to commit
arrange("William").waitFirst(5).do("push").execute();
// > 等待5秒
// > William is notified
// > Start to push
这个函数有几个特点:
- 可以链式调用(一个接一个地调用方法)。
- 可以延迟执行某些操作。
- 只有调用
execute()
方法时,才会按顺序执行所有操作。
思路
首先我们可以看到,无论是函数本身,还是其他链式方法,函数本身不会引发什么执行,而只有等到最后调用 execute()
的时候才会执行。所以,无论是函数本身还是 do
还是 waiit
还是其他链式方法也好,它只是把一个任务加到某个 队列 里面去等待,等着它调用 execute
函数,再去把这个队列循环一遍执行。
所以,这个函数本身应该有一个队列并且自身需要往队列添加任务:
function arrange(name) {
const tasks = [];
tasks.push(() => {
console.log(`${name} is notfied`);
});
}
但我们需要考虑到后续还有其他方法的调用,所以这个函数需要返回一个对象,这个对象包含多个可以被链式调用的方法:
function arrange(name) {
...
/* chain methods */
function doSomething(action: string) {}
function wait(sec) {}
function waitFirst(sec) {}
function execute() {}
return {
do: doSomething,
wait,
waitFirst,
execute
}
}
接着,我们继续填充几个链式方法的内容:
function arrange(name) {
const tasks = []
tasks.push(() => {
console.log(`${name} is notfied`
})
function doSeomthing(action) {
tasks.push(() => {
console.log(`Start to ${action}`)
})
}
function wait(sec) {
tasks.push(() => new Promise(resolve => {
setTimeout(resolve, sec * 1000)
}))
}
function waitFirst(sec) {
tasks.push(() => new Promise((resolve) => {
setTimeout(resolve, sec * 1000)
}))
}
function execute() {}
return {
do: doSeomthing,
wait,
waitFirst,
execute
}
}
我们根据执行结果发现,当调用waitFirst
时它总会在第一个时候执行,所以,我们需要让它放在队列的第一项:
function waitFirst(sec) {
tasks.unshift((() => {
...
}))
}
接着,execute
方法会将队列中所有的任务都拿出来执行一遍:
async function execute() {
for (const task of tasks) {
await task();
}
}
可写到这里并不能够满足需求,因为当我们执行了 do
或者 wait
等方法,我们在这后面还能接着调用其他链式方法,所以,这说明每一个函数执行完了之后,都需要再将链式对象返回:
function doSomething(action) {
...
return this
}
function wait(sec) {
...
return this
}
这里,我们使用了 this
关键字,将整个 arrange
上下文返回,这样就不用重新赋值一个变量,并且返回this
能够让所有链式方法都拥有函数上下文,都可以使用链式调用。
完整代码
type Task = () => Promise<void> | void;
interface Arrange {
do: (action: string) => Arrange;
wait: (seconds: number) => Arrange;
waitFirst: (seconds: number) => Arrange;
execute: () => Promise<void>;
}
function arrange(name: string): Arrange {
const tasks: Task[] = [];
tasks.push(() => {
console.log(`${name} is notified`);
});
const api: Arrange = {
do(action: string) {
tasks.push(() => {
console.log(`Start to ${action}`);
});
return this;
},
wait(seconds: number) {
tasks.push(() => {
return new Promise<void>(resolve => {
setTimeout(resolve, seconds * 1000);
});
});
return this;
},
waitFirst(seconds: number) {
tasks.unshift(() => {
return new Promise<void>(resolve => {
setTimeout(resolve, seconds * 1000);
});
});
return this;
},
async execute() {
for (const task of tasks) {
await task();
}
},
};
return api;
}
// 测试代码
arrange("William").waitFirst(2).do("commit").wait(3).do("push").execute();