阿里异步串行编程题:按照以下要求,实现 createFlow 函数
sisterAn opened this issue · comments
瓶子君 commented
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const subFlow = createFlow([() => delay(1000).then(() => console.log("c"))]);
createFlow([
() => console.log("a"),
() => console.log("b"),
subFlow,
[() => delay(1000).then(() => console.log("d")), () => console.log("e")],
]).run(() => {
console.log("done");
});
// 需要按照 a,b,延迟1秒,c,延迟1秒,d,e, done 的顺序打印
按照上面的测试用例,实现 createFlow
:
flow
是指一系列effects
组成的逻辑片段。flow
支持嵌套。effects
的执行只需要支持串行。
瓶子君 commented
在本题中, createFlow
以一个数组作为参数,数组参数可有以下几种类型:
- 普通函数
() => console.log("a")
- 异步函数
() => delay(1000).then(() => console.log("c"))
- 嵌套
createFlow
const subFlow = createFlow([() => delay(1000).then(() => console.log("c"))])
- 数组
[() => delay(1000).then(() => console.log("d")), () => console.log("e")]
步骤一:扁平化
因为 effects
的执行只需要支持串行,所以我们可以把数组扁平化一下
function createFlow(effects = []) {
// 浅拷贝一下,今年不影响传入的参数
const queue = [...effects.flat()]
}
步骤二:run执行
createFlow
并不是直接执行,而是 .run()
之后才会开始执行
function createFlow(effects = []) {
const queue = [...effects.flat()]
const run = async function(cb) {
for(let task of queue) {
}
if(cb) cb()
}
}
步骤三:异步函数
因为参数中有异步函数,这里使用 await
执行
function createFlow(effects = []) {
const queue = [...effects.flat()]
const run = async function(cb) {
for(let task of queue) {
await task()
}
if(cb) cb()
}
}
步骤四:支持嵌套
使用 isFlow
来判断当前是否是嵌套执行
function createFlow(effects = []) {
const queue = [...effects.flat()]
const run = async function(cb) {
for(let task of queue) {
if(task.isFlow) { // 如果是嵌套,执行嵌套函数
await task.run()
} else {
await task()
}
}
if(cb) cb()
}
return {
run,
isFlow: true
}
}
其实是可以使用 class
来实现,用 instanceof
来判断是否是嵌套,代码实现如下:
class Flow {
constructor(props) {
this.queue = props
}
async run(cb) {
for(let task of this.queue) {
if(task instanceof Flow) {
await task.run()
} else {
await task()
}
}
if(cb) cb()
}
}
function createFlow(effects = []) {
return new Flow(effects)
}
完整代码:两种
- function
function createFlow(effects = []) {
const queue = [...effects.flat()]
const run = async function(cb) {
for(let task of queue) {
if(task.isFlow) {
await task.run()
} else {
await task()
}
}
if(cb) cb()
}
return {
run,
isFlow: true
}
}
// 测试
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const subFlow = createFlow([() => delay(1000).then(() => console.log("c"))]);
createFlow([
() => console.log("a"),
() => console.log("b"),
subFlow,
[() => delay(1000).then(() => console.log("d")), () => console.log("e")],
]).run(() => {
console.log("done");
});
// a,b,延迟1秒,c,延迟1秒,d,e, done 的顺序打印
- class
class Flow {
constructor(effects) {
this.queue = [...effects.flat()]
}
async run(cb) {
for(let task of this.queue) {
if(task instanceof Flow) {
await task.run()
} else {
await task()
}
}
if(cb) cb()
}
};
function createFlow(effects = []) {
return new Flow(effects)
};
// 测试
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const subFlow = createFlow([() => delay(1000).then(() => console.log("c"))]);
createFlow([
() => console.log("a"),
() => console.log("b"),
subFlow,
[() => delay(1000).then(() => console.log("d")), () => console.log("e")],
]).run(() => {
console.log("done");
});
// a,b,延迟1秒,c,延迟1秒,d,e, done 的顺序打印
7yue commented
const createFlow = (lists) => {
return new Promise((re, rj) => {
let i=0
let len = lists.length
const next = (i)=>{
if(i == lists.length) return re()
let item = lists[i]
if(Array.isArray(item)) {
createFlow(item).then(() => next(i+1))
}else if(item instanceof Promise) {
Promise.resolve(item).then(() => next(i+1))
}else if(typeof item == 'function') {
let res = item()
if(res instanceof Promise){
Promise.resolve(res).then(() =>next(i+1))
}else{
next(i+1)
}
}else {
next(i+1)
}
}
next(i)
})
}
Promise.prototype.run = function(cb) {
this.then(cb)
}
const delay = ms => new Promise(re => setTimeout(re, ms))
const subFlow = createFlow([() => delay(1000).then(() => console.log('c'))])
createFlow([
() => console.log('a'),
() => console.log('b'),
subFlow,
[() => delay(1000).then(() => console.log('d')), () => console.log('e')],
]).run(() => console.log('done'))
7yue commented
之前会错意了,思路一样,利用 Promise 异步串行
const createFlow = (lists) => {
let tasks = [...lists].flat()
const run = (cb) => {
let [first, ...others] = [...tasks]
return others.reduce((promise, task) => {
task = task.run ? task.run : task
return Promise.resolve(promise).then(_ => task())
}, first()).then(cb)
}
return { run }
}
kexiaofu commented
其实我还有一个思路:
-
收集参数部分相同;
-
在 run 的时候,判断 task.then 是否是可执行的方法,请看代码
function isType(target, type) {
return (Object.prototype.toString.call(target) || '').indexOf(type) > -1;
}
function log(msg) {
return console.log(msg);
}
function createFlow(arr) {
let task = [];
arr.forEach(item => {
if (isType(item, 'Function')) {
if (item.name === 'createFlow') {
task = task.concat(item.task);
} else {
task.push(item);
}
}
if (isType(item, 'Array')) {
task = task.concat(item);
}
})
createFlow.task = task;
createFlow.run = function (fn) {
if (this.task.length > 0) {
const func = task.shift();
const result = func();
// 这里判断
if (result && typeof result.then === 'function') {
result.then(() => this.run(fn));
} else {
this.run(fn);
}
} else {
typeof fn === 'function' && fn();
}
}
return createFlow;
}
xiaowuge007 commented
function createFlow(arr) {
let curList = [];
add(arr);
return {
run: function (cb) {
if(cb){
curList.push(cb);
}
go(curList);
},
_list: curList
}
async function go(list) {
for(let i = 0;i<list.length;i++){
await list[i]();
}
}
function add(list) {
for(let i = 0;i<list.length;i++){
let item = list[i];
if(typeof item === 'function'){
curList.push(item)
}
if(Array.isArray(item)){
add(item)
}
if(typeof item === 'object' && item._list){
add(item._list)
}
}
}
}
NoBey commented
function createFlow(arr){
arr.run = async (cb) => {
for(let fn of arr.flat(99)){
await fn()
}
cb && cb()
}
return arr
}
Athaoo commented
不简洁但是把步骤拆分成了collect和run两个阶段,不进行flat
class Flow {
constructor(task) {
this.task = task
}
async run(cb) {
await this.task()
cb instanceof Function && cb()
}
}
const runFlow = async (flow) => {
if (flow instanceof Array) {
for (const item of flow) {
await runFlow(item)
}
} else if (flow instanceof Function) {
await runFlow(flow())
} else if (flow instanceof Flow) {
await flow.run()
} else if (flow instanceof Promise) {
await flow
} else {
// static val, do nothing
return
}
}
const collectFlow = (flow, list = []) => {
list.push(() => runFlow(flow))
return list
}
export const createFlow = (flow) => {
return new Flow(() => runFlow(collectFlow(flow, [])))
}
front-end-captain commented
type Effect = (() => Promise<void> | void) | Flow;
class Flow {
private _queue: (() => void)[];
private _endCallback: (() => void) | undefined;
constructor(effects: Effect[]) {
this._queue = effects.map((effect) => {
const fn = () => {
Promise.resolve(effect instanceof Flow ? effect.run() : effect()).finally(() => {
this._next();
});
};
return fn;
});
}
private _next() {
const fn = this._queue.shift();
if (fn) {
fn();
} else if (this._endCallback) {
this._endCallback();
}
}
public run(callback?: () => void): void {
this._endCallback = callback;
this._next();
}
}
function createFlow(effects: (Effect | Effect[])[]) {
return new Flow(effects.flat());
}
const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
const subFlow = createFlow([() => delay(1000).then(() => console.log("c"))]);
createFlow([
() => console.log("a"),
() => console.log("b"),
subFlow,
[() => delay(1000).then(() => console.log("d")), () => console.log("e")],
]).run(() => {
console.log("done");
});