【发布时间】:2021-11-05 01:07:19
【问题描述】:
我试图解决一个关于递归和异步的编码问题,但我有点卡住了,这就是问题所在:
任务
您必须执行许多任务。任务就是任何函数(通常是异步的)。
有些任务可以相互依赖。所以他们必须等到他们所依赖的任务首先完成。
您必须等到所有任务都完成并返回它们的结果。
输入
以任务 ID 作为键,将任务描述为值的对象:
interface TaskDict {
[taskId: string]: {
dependencies: string[]; // an array of task ids.
task: (...dependencyResults: any[]) => any;
}
}
输出
使用任务 ID 作为键、任务结果作为值的对象进行解析的承诺:
interface TaskResultDict {
[taskId: string]: (
{
status: 'resolved',
value: any
} |
{
status: 'failed',
reason: any
} |
{
status: 'skipped',
unresolvedDependencies: string[]
}
);
}
请注意,如果任务的任何依赖项未解决(例如失败或依次跳过),则不应执行任务。
在这种情况下,状态将为skipped。
如果依赖项是循环的,则应该是相同的skipped 状态。是的,输入中可能存在此错误。除此之外,输入将始终有效(无需编写验证)。
示例
const {deepStrictEqual} = require('assert');
const runTasks = (tasks: TaskDict): Promise<TaskResultDict> => {
// TODO
};
const taskResults = await runTasks({
a: {
dependencies: [],
task: () => Promise.resolve(4)
},
b: {
dependencies: ['a', 'c'],
task: async (a, c) => Math.sqrt(c * c - a * a)
},
c: {
dependencies: [],
task: () => new Promise((x) => setTimeout(x, 100)).then(() => 5)
},
d: {
dependencies: [],
task: () => Promise.reject('This will fail.')
},
e: {
dependencies: ['d', 'a', 'f'],
task: console.log
},
f: {
dependencies: ['f'],
task: () => console.log('Should never run - "f" depends on itself.')
}
});
deepStrictEqual(taskResults, {
a: {status: 'resolved', value: 4},
b: {status: 'resolved', value: 3},
c: {status: 'resolved', value: 5},
d: {status: 'failed', reason: 'This will fail.'},
e: {status: 'skipped', unresolvedDependencies: ['d', 'f']},
f: {status: 'skipped', unresolvedDependencies: ['f']}
});
目前的做法
到目前为止,我已经采用了这种方法,但主要问题是只要我不使用异步任务来解决,逻辑就可以正常工作,在这种情况下,异步流程无法按我的预期工作,所以在执行某些任务之前要解决的依赖项没有正确解决,您有什么线索吗?
const resolveDependency = async (
taskId: string,
task: (...dependencyResults: any[]) => any,
dependencies: string[],
results: TaskResultDict
): Promise<TaskResultDict> => {
const unresolvedDependencies = Object.entries(results).filter(
(result) =>
dependencies.includes(result[0]) && result[1].status !== 'resolved'
);
try {
if (unresolvedDependencies.length > 0) {
return ({
[taskId]: {
status: 'skipped',
unresolvedDependencies: unresolvedDependencies.map(
(dependency) => dependency[0]
),
},
} as TaskResultDict);
}
const taskValue = await task(
...Object.entries(results)
.filter((result) => dependencies.includes(result[0]))
.map(
(result) => (result[1] as { status: 'resolved'; value: any }).value
)
);
return ({
[taskId]: {
status: 'resolved',
value: taskValue,
},
} as TaskResultDict);
} catch (error) {
return ({
[taskId]: {
status: 'failed',
reason: error,
},
} as TaskResultDict);
}
};
const runTaskWithDependencies = async (
tasks: TaskDict,
taskId: string,
results: TaskResultDict
): Promise<TaskResultDict> => {
const taskDependencies = tasks[taskId].dependencies;
const allDependenciesExecuted = Object.keys(results).length > 0 && Object.keys(results).every((taskId) =>
taskDependencies.includes(taskId)
);
if (taskDependencies.includes(taskId)) {
return {
[taskId]: {
status: 'skipped',
unresolvedDependencies: [taskId],
},
};
} else if (allDependenciesExecuted || taskDependencies.length === 0) {
const taskResult = await resolveDependency(
taskId,
tasks[taskId].task,
taskDependencies,
results
);
return {
...results,
...taskResult,
};
} else {
return (
await Promise.all(
taskDependencies
.map(
(dependency) =>
runTaskWithDependencies(tasks, dependency, results)
)
)
).reduce((previous, current) => {
return {
...previous,
...current,
};
}, {} as TaskResultDict);
}
};
export const runTasks = async (tasks: TaskDict): Promise<TaskResultDict> => {
const tasksIds = Object.keys(tasks);
return await tasksIds.reduce(async (previous, current) => {
const taskResult = await runTaskWithDependencies(
tasks,
current[0],
await previous
);
return {
...previous,
...taskResult,
};
}, Promise.resolve(<TaskResultDict>{}));
};
【问题讨论】:
-
感觉就像您正在尝试将所需的一切都放入一个函数中。您可能应该尝试通过几个更简单的步骤来减少它。例如,我建议在尝试开始处理任务之前处理循环依赖。
标签: javascript typescript asynchronous promise