предисловие
Друг пошел на собеседование в Ant Financial и столкнулся с вопросом для интервью, который на первый взгляд казался очень простым, но, осознав его, я обнаружил, что есть еще немало моментов, о которых стоит упомянуть.
Сначала посмотрите тему:
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const subFlow = createFlow([() => delay(1000).then(() => log("c"))]);
createFlow([
() => log("a"),
() => log("b"),
subFlow,
[() => delay(1000).then(() => log("d")), () => log("e")],
]).run(() => {
console.log("done");
});
// 需要按照 a,b,延迟1秒,c,延迟1秒,d,e, done 的顺序打印
В соответствии с приведенным выше тестовым случаем реализоватьcreateFlow
:
-
flow
означает рядeffects
состоит из логических фрагментов. -
flow
Вложенность поддерживается. -
effects
Реализация должна поддерживать только последовательный порт.
анализировать
Сначала введите анализ параметров,createFlow
Принимает в качестве параметра массив (по смыслу вопроса каждый элемент должен называтьсяeffect
), чтобы исключить некоторые повторяющиеся элементы, мы систематизируем и классифицируем каждый элемент в массиве параметров, всего есть следующие типы:
- Обычная функция:
() => log("a");
- Функция задержки (обещание):
() => delay(1000).then(() => log("d"));
- еще один
flow
:
const subFlow = createFlow([() => delay(1000).then(() => log("c"))]);
- Вышеупомянутые три элемента завернуты в массив.
выполнить
Сначала сделайте поверхностную копию параметров (при написании библиотечных функций старайтесь не влиять на параметры, переданные пользователем, это принцип), а затем просто сгладьте их.flat
один раз. (обработка случая 4)
function createFlow(effects = []) {
let sources = effects.slice().flat();
}
соблюдать смысл,createFlow
не заставит метод начать выполнение, он должен быть.run()
Выполнение начнется позже, поэтому сначала определите эту функцию:
function createFlow(effects = []) {
let sources = effects.slice().flat();
function run(callback) {
while (sources.length) {
const task = sources.shift();
}
callback?.();
}
}
Здесь я предпочитаю использоватьwhile
Перебрать каждый из массивов по очередиeffect
, так что его можно прервать в любой момент.
для типов функцийeffect
, выполните его напрямую:
function createFlow(effects = []) {
let sources = effects.slice().flat();
function run(callback) {
while (sources.length) {
const task = sources.shift();
if (typeof task === "function") {
const res = task();
}
}
// 在所有任务执行完毕后 执行传入的回调函数
callback?.();
}
return {
run,
isFlow: true,
};
}
Вот возвращаемое значение функцииres
, есть одна ситуация не забывайте, чтоeffect
возвращаетPromise
, например в этом случае:
() => delay(1000).then(() => log("d"));
Затем, после получения возвращаемого значения, мы можем напрямую упростить суждение здесь, чтобы увидеть, имеет ли возвращаемое значение атрибут then, чтобы определить, является ли оно обещанием (пожалуйста, выберите более строгий метод для производственной среды).
if (res?.then) {
res.then(createFlow(sources).run);
return;
}
Здесь я решил прервать на этот разflow
выполнить и использовать оставшиесяsources
создать новыйflow
, и асинхронно открыть новый в методе then предыдущего промисаflow
изrun
.
Таким образом, после того, как вышеуказанный Promise отложен на Resolve 1s, оставшиесяsources
Количество заданий будет новымflow.run()
езжайте и продолжайте.
разобраться со следующимeffect
Другойflow
, обратите внимание на написанное выше примерное тело функции, мы сделалиcreateFlow
Эта функция возвращает значение сisFlow
Этот флаг используется для определения того, является лиflow
.
// 把callback放到下一个flow的callback时机里执行
const next = () => createFlow(sources).run(callback)
if (typeof task === "function") {
const res = task();
if (res?.then) {
res.then(next);
return;
}
} else if (task?.isFlow) {
task.run(next);
return;
}
Смотретьelse if
часть, которая напрямую вызывает переданныйflow
изrun
, положить остальноеsources
создал новыйflow
, и поместите этот раундcallback
положить в новыйflow
изcallback
Место нахождения. Выполнять после завершения всех задач.
определитьnext
метод, используемый при обнаружении асинхронной задачи или другогоflow
когда
Таким образом, переданный параметрflow
После завершения выполнения мы продолжим реализацию оставшихся задач, а в финальном исполненииcallback
.
полный код
function createFlow(effects = []) {
let sources = effects.slice().flat();
function run(callback) {
while (sources.length) {
const task = sources.shift();
// 把callback放到下一个flow的callback时机里执行
const next = () => createFlow(sources).run(callback)
if (typeof task === "function") {
const res = task();
if (res?.then) {
res.then(next);
return;
}
} else if (task?.isFlow) {
task.run(next);
return;
}
}
callback?.();
}
return {
run,
isFlow: true,
};
}
const delay = () => new Promise((resolve) => setTimeout(resolve, 1000));
createFlow([
() => console.log("a"),
() => console.log("b"),
createFlow([() => console.log("c")]),
[() => delay().then(() => console.log("d")), () => console.log("e")],
]).run();
Суммировать
Основная цель этого вопроса для интервью — изучить управление асинхронными последовательными потоками, а также разумное использование собственного рекурсивного дизайна для обработки входящих параметров.flow
В процессе написания вопроса демонстрация того, как вы умело используете обещания, определенно произведет впечатление на интервьюера~
Желаю вам всем получить предложение, которое вас устроит в плохой среде.