DarkRiDDeR12 мин

Разбор объяснения Promise.all: как вернуть рецепт к условиям задачи

JavaScriptПрактика

В разборе объяснения чаще всего виден готовый рецепт: «оберни массив в Promise.all», но отсутствуют условия, при которых результат нужен и безопасен. Цена ошибки — hand-off с неявной ложью об отмене: следующий инженер копирует фрагмент в другую границу и тратит время на последствия, которые текст обещал убрать.

Причина в том, что обратная связь проверяет наличие API-имени, а не причинную модель. Проверка должна быть короткой: есть ли исходная задача, модель aggregate, контрпример и ответ про отмену. Действие — вернуть не оценку личности, а fixed synthetic карточку с одним конкретным пробелом и упражнением на его закрытие.

Полевая ситуация без реальных людей

Возьмём fixed attempt recipe-only-v1. В нём задача названа, но поля model и counterexample пусты; ответ не фиксирует границу отмены. Это не запись о кандидате, pull request или учебной группе. Это маленький объект, которым можно воспроизводимо проверить саму форму объяснения. Его ценность в том, что другой редактор получит те же причины, а не будет угадывать настроение автора.

Замкнутый цикл обратной связи: fixed attempt проходит проверку модели, контрпримера и границы; конкретный пробел возвращается как короткое упражнение, а положительный исход становится только synthetic hand-off.
Цикл не измеряет обучение. Он делает следующий технический шаг и причину возврата наблюдаемыми.
Сигнал в объяснении и точный feedback
СигналДиагнозКороткое упражнениеРезультат
есть только await Promise.allmissing-modelназвать aggregate и inputsreturn-with-specific-gap
нет оставшегося inputmissing-counterexampleпрочитать trace из двух строкreturn-with-specific-gap
«всё отменяется»wrong-boundaryнайти отдельный cancel contractreturn-with-specific-gap
все три части естьмодель проверяемапередать ограничениеsynthetic-ready-for-human-review

Исполняемая карточка проверки

import { createFixedTeachingAttempt, checkFixedTeachingAttempt } from './upgrade-2025-10.mjs';

const attempt = createFixedTeachingAttempt('recipe-only-v1');
const report = checkFixedTeachingAttempt(attempt);
console.log(report.reasons);
// ['missing-model', 'missing-counterexample', 'wrong-boundary']

Проверка намеренно не принимает произвольный объект как доказательство. Она принимает только frozen fixed literals этого модуля. Так не возникает видимости, будто мы валидируем реальные объяснения или строим универсальную систему оценки. В ответе есть effect: no-system-change; функция не создаёт задачу, не пишет комментарий и не меняет состояние вне памяти.

Как дать feedback, который можно выполнить

Неполезный feedback: «раскрой тему глубже». Он не называет наблюдение и даёт автору слишком много вариантов. Полезный feedback: «в строке с Promise.all добавь модель aggregate и один контрпример: после aggregate reject remaining input может завершиться; затем назови, какой отдельный contract отвечает за отмену». В нём есть объект, условие, ожидаемое изменение и граница, которую нельзя додумать автоматически.

Короткая последовательность разбора

  1. Зафиксируйте исходную задачу одной фразой, не начиная с имени API.
  2. Найдите модель: входы, aggregate и зависимый расчёт.
  3. Попросите один контрпример, который ломает слишком широкий рецепт.
  4. Запустите fixed exercise и сравните trace с выводом автора.
  5. Верните только конкретные gaps или synthetic hand-off для human review; не объявляйте результат доказательством обучения.

Когда рецепт допустим

Рецепт полезен, когда модель уже названа и условия узкие. Например, после объяснения можно показать await Promise.all([left, right]) как способ дождаться обоих значений перед зависимым расчётом. Но рядом остаётся ограничение: этот вызов не описывает отмену внешних действий и не выбирает обработку каждого результата. Если нужны все outcomes, вопрос меняется, и читатель должен сравнить его с Promise.allSettled, а не механически заменить имя.

Проверяемый следующий шаг

Проверьте у собственного объяснения только четыре предложения: задача, модель, контрпример, отдельный владелец отмены. Если одно отсутствует, не добавляйте ещё один фрагмент кода — заполните конкретный пробел. Положительный исход этого упражнения ограничен: model-first-v1 получает synthetic-ready-for-human-review. Он не доказывает улучшение человека, команды или production-кода.

Три вопроса для независимого читателя

Независимый читатель не должен угадывать намерение автора. Поэтому после feedback дайте ему три вопроса без подсказки: «что вернёт aggregate при первой ошибке?», «что показывает второй элемент trace?» и «какой объект имеет право остановить внешнюю работу?». Ответы проверяют не запоминание имени метода, а наличие границы. Если третий ответ заменяется словом Promise.all, возвращаем только gap wrong-boundary.

Такой способ удобно применять к собственной документации. Он не требует реального класса, оценки кандидата или pull request. Достаточно fixed примера и редакторского чтения. Важна дисциплина формулировки: feedback сообщает, что нужно добавить в текст, а не что автор «не понимает async». Это сокращает спор о тоне и оставляет один проверяемый технический предмет для следующей версии объяснения.

Граница обратной связи
Можно проверитьНельзя заключитьБезопасный следующий шаг
trace имеет два разных событияреальная операция завершиласьописать synthetic модель
ответ называет aggregateавтор применит знание вездедобавить контрпример
gap конкретенчеловек стал сильнеепередать на human review
нет side effectproduction безопасенотдельно проектировать API

Все карточки, попытки, ответы, результаты и hand-off ниже — fixed synthetic JS literals в памяти. Модуль не читает files, сеть, Git, CI, clock, telemetry, production data или user data и не выполняет side effect.

Проверяемые источники

  • ECMA-262, 16th edition (June 2025), §27.2.4.1 Promise.all — версия: 16th edition, June 2025, immutable published PDF. Алгоритм Promise.all создаёт одну capability, обходит iterable и передаёт его элементы в PerformPromiseAll; результат завершается ошибкой через reject capability. Граница: Спецификация описывает семантику языка. Она не проектирует отмену работы, timeout, retry или API конкретного приложения.
  • MDN Promise.all(), immutable content commit 3fad0447 (19 August 2025) — версия: mdn/content commit 3fad0447b4901e28fe88769976787d8d8b87d66d, 2025-08-19. Promise.all выполняется успешно после выполнения всех входов либо отклоняется на первой ошибке; результат успеха сохраняет порядок входного iterable, а allSettled ждёт все исходы. Граница: Документация не утверждает, что Promise.all отменяет уже начатую внешнюю работу или заменяет протокол остановки.