Last active
March 5, 2020 15:06
-
-
Save hjkcai/d8caada37ec459af2198a788ff2f3b6c to your computer and use it in GitHub Desktop.
2018-3-2 面试题
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 实现一个方法 parallel(tasks, concurrency),让 tasks 并发执行(并控制并发数为 concurrency) | |
// 其中 tasks 为一个数组,每一个元素都是一个方法返回一个 promise | |
// 当所有 tasks 执行完成时,resolve 一个数组保存所有的结果 | |
// 当任意一个 task 执行失败时,reject 这个错误 | |
// 运行方法: | |
// $ tsc parallel.ts --target esnext && mocha parallel | |
declare const require | |
declare const describe | |
declare const it | |
const assert = require('assert') | |
type Task<T = any> = () => Promise<T> | |
function parallel<T> (tasks: Task<T>[], concurrency: number): Promise<T[]> { | |
// 输入检查 | |
concurrency = Math.floor(concurrency) | |
if (Number.isNaN(concurrency) || concurrency <= 0) { | |
return Promise.reject(new Error('Invalid parameter')) | |
} | |
/** 储存并行任务结果 */ | |
const result: T[] = tasks.slice() as any[] | |
/** 最后一个未开始的任务 */ | |
let index = 0 | |
// 并行执行任务 | |
const parallelTasksRunner = Array.from({ length: concurrency }, async () => { | |
while (index < result.length) { | |
const i = index++ | |
result[i] = await tasks[i]() | |
} | |
}) | |
// 确保所有任务执行完成,并返回结果 | |
return Promise.all(parallelTasksRunner).then(() => result) | |
} | |
describe('parallel', () => { | |
function delayTask (input: any, ms: number): Task { | |
return () => new Promise(resolve => { | |
setTimeout(() => resolve(input), ms) | |
}) | |
} | |
function createDelayTasks (): Task[] { | |
return [ | |
delayTask(1, 100), | |
delayTask(2, 100), | |
delayTask(3, 0), | |
delayTask(4, 200), | |
delayTask(5, 200) | |
] | |
} | |
function assertDelayTasks (values: any[]): void { | |
assert.deepEqual(values, [1, 2, 3, 4, 5]) | |
} | |
async function measureTime (task: Promise<any>): Promise<number> { | |
const start = Date.now() | |
await task | |
return Date.now() - start | |
} | |
describe('保证所有任务完成,且顺序正确', () => { | |
for (let i = 1; i <= 5; i++) { | |
it(`concurrency = ${i}`, () => { | |
return parallel(createDelayTasks(), i).then(assertDelayTasks) | |
}) | |
} | |
}) | |
describe('保证同时运行的任务数与设置的一致(允许 5% 误差)', () => { | |
it('concurrency = 1', async () => { | |
const time = await measureTime(parallel(createDelayTasks(), 1)) | |
assert(time >= 600 && time < 630) | |
}) | |
it('concurrency = 2', async () => { | |
const time = await measureTime(parallel(createDelayTasks(), 2)) | |
assert(time >= 300 && time < 315) | |
}) | |
it('concurrency = 3', async () => { | |
const time = await measureTime(parallel(createDelayTasks(), 3)) | |
assert(time >= 300 && time < 315) | |
}) | |
it('concurrency = 4', async () => { | |
const time = await measureTime(parallel(createDelayTasks(), 4)) | |
assert(time >= 200 && time < 210) | |
}) | |
it('concurrency = 5', async () => { | |
const time = await measureTime(parallel(createDelayTasks(), 5)) | |
assert(time >= 200 && time < 210) | |
}) | |
}) | |
describe('其它情况', () => { | |
it('没有任务时也能正常结束', () => { | |
return parallel([], 5).then(result => assert.deepEqual(result, [])) | |
}) | |
it('任务没返回 Promise 也能正常结束', () => { | |
return parallel([ | |
() => 1, | |
() => 2, | |
() => 3, | |
delayTask(4, 200), | |
delayTask(5, 200) | |
] as any, 2).then(assertDelayTasks) | |
}) | |
it('并行数量过多时也能正确完成任务', () => { | |
return parallel(createDelayTasks(), 10).then(assertDelayTasks) | |
}) | |
it('并行数量无效时抛错', () => { | |
return parallel(createDelayTasks(), 0).then(result => { | |
assert.fail('并行数量无效时应抛错') | |
}).catch(err => { /* noop */ }) | |
}) | |
it('任务无效时抛错', () => { | |
return parallel([1, 2, 3] as any, 1).then(result => { | |
assert.fail('任务无效时应抛错') | |
}).catch(err => { /* noop */ }) | |
}) | |
}) | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment