Я переписал код, устраняя все не относящиеся к делу вещи и используя стиль, который я чувствую, более читабельным и умиротворяющим в этом контексте.
function doAsynchronousStuff()
{
return new Promise((resolve, reject) =>
{
setTimeout(() => {resolve("test")}, 0)
})
.then(console.log)
.then(doAsynchronousStuff);
}
Мы должны анализировать поток выполнения запоминанием, что JS имеет an event loop, в частности
setTimeout
сообщений аргумент функции будет выполняться на следующем цикла цикла событий.
then
публикует свою функцию аргумента, которая должна быть выполнена в следующем цикле цикла события.
Существование цикла событий важно, так как сообщение функции проводки на это вводных к завершению до цикла повторно введено.
Также требуется хорошее знание обещаний, например, зная, что then
возвращает новое обещание.
Когда doAsynchronousStuff
выполнен, объект Promise
построен и его функция аргумента вызывается немедленно.
Execution stack Event loop messages
doAsynchronousStuff
Promise constructor
Closure (resolve, reject)
Это в свою очередь вызывает setTimeout
что пост событие и возвращается.
Execution stack Event loop messages
doAsynchronousStuff resolve("test")
Promise constructor
Closure (resolve, reject)
setTimeout
Казни возвращается к doAsynchronousStuff
, которые устанавливают продолжения для Promise
объектов, но не выполняют их, конечно. Таким образом, в конце doAsynchronousStuff
возвращается, и у нас есть Замедление до завершения.
Execution stack Event loop messages
resolve("test")
Цикл событий выполняет resolve("test")
(или лучше замыкание, которое содержит его), который устанавливается обещание как решенный и планировать свое продолжение на следующем цикле
Execution stack Event loop messages
resolve console.log
resolve
заканчивается у нас снова пробега -to-completion положение дел.
Execution stack Event loop messages
console.log
console.log
выполнено. Фактически, выполняется функция, вызывающая console.log
, эта функция задается объектом-обещанием при вызове then
.
Когда console.log
возвращает свое обещание, решает, и doAsynchronousStuff
отправляется на контур события.
Execution stack Event loop messages
resolve doAsynchronousStuff
Когда resolve
заканчивается у нас есть вводного к завершению и doAsynchronousStuff
выполняется еще раз.
Теперь я не буду копать слишком много в математическом смысле, ни в CS теоретическом смысле слова в списке в вашем вопросе, делая такие не будет иметь никаких практических преимуществ, поскольку я не считаю, что это теоретический вопрос.
Вместо этого я буду ограничиваться программированием.
К тому времени, когда второй экземпляр doAsynchronousStuff
называется первым, он давно ушел (он завершение до завершения).
В принципе ситуация равносильна делает этот
let f =() => { console.log('hi!'); setTimeout(f, 0); }
Я бы не назвал эту функцию рекурсивного так рекурсия подразумевает разрушение задачи на более мелкие части авто автомодельного.
Рекурсивная функция не должна называть себя непосредственно или не должна «увеличивать стек», но она должна быть , определенной в терминах самого себя.
Если бы это было как
let f =() => { f(); }
Я бы назвал это (плохо) рекурсивным. Так что это?
Я хотел бы сказать, что функция является рекурсивной в смысле программирования, если вы не можете ее завершить, не выполняя все вызовы, которые она сама делает.
Первый пример может быть завершен, не дожидаясь завершения последующих вызовов f
, второй вместо этого не может.
На мой взгляд, я называю первую версию f
, запланировано.
Относительно Оптимизация хвостового вызова, это не имеет никакого отношения к этому.
TCO transform a particular kind of recursion into a loop, это оптимизация компилятора не является свойством кода.
tail-call является собственностью кода, но этот код не является tail-call, поскольку он не является рекурсивным в первую очередь.
Также не итерация в смысле программирования (в то время как в теоретическом смысле), так как итерация достигается специфическими конструкциями (например, for
, while
, goto
).
Граница размыта здесь как итерация, рекурсия и планирование перекрытий.
Наконец, это, безусловно, относится к процедуре без прерывания, которая, как считается, относится к самому себе.
Мы делаем здесь упрощение, он не должен быть в очень следующий цикл, это просто будущий цикл.
TCO является свойством среды выполнения/компилятора, а не кода. Таким образом, элемент TCO просто неприменим (пока он не будет переписан). – zerkms
@zerkms Ищет ясность относительно различий перечисленных терминов, и на какой срок следует ссылаться на Вопрос в качестве; чтобы избежать путаницы. Можете ли вы опубликовать ответ на вопрос? – guest271314
Это каждый из 1,3,4. – zerkms