Популярные задачки
1 Promise order (opens in a new tab)
console.log(1)
const promise = new Promise((resolve) => {
console.log(2)
resolve()
console.log(3)
})
console.log(4)
promise.then(() => {
console.log(5)
}).then(() => {
console.log(6)
})
console.log(7)
setTimeout(() => {
console.log(8)
}, 10)
setTimeout(() => {
console.log(9)
}, 0)
Ответ: 1, 2, 3, 4, 7, 5, 6, 9, 8
console.log(1)
выполняется первым, так как управление происходит синхронно.console.log(2)
выполняется следующим в рамках определенияPromise
.- Выполняется
resolve()
, но обработкаPromise (.then())
происходит в очереди микрозадач, которая выполняется после завершения текущей задачи. console.log(3)
выполняется дальше, поскольку он все еще является частью определенияPromise
, которое выполняется синхронно.console.log(4)
выполняется в рамках основного управления.- Далее выполняется
console.log(7)
. - После завершения основного потока управления следующий элемент в очереди микрозадач выбирается. Здесь выполняется
console.log(5)
. Строка с.then()
создает еще один промис, который снова попадает в очередь микрозадач. - Затем обрабатывается вторая микрозадача, выводится
console.log(6)
. - Когда все микрозадачи завершены, мы имеем дело с
setTimeout
с задержкой 0. Но несмотря на то, что время равно 0, это все равно помещает обратный вызов в очередь макрозадач, которая обрабатывается только после выполнения всех микрозадач. Итак, выводитсяconsole.log(9)
. - В конце концов,
setTimeout
с задержкой 10 мс будет последним, так как он отправляется в API и ожидает 10 мс, прежде чем его поместят в очередь макрозадач. В итоге выводитсяconsole.log(8)
.
2 promise executor (opens in a new tab)
new Promise((resolve, reject) => {
resolve(1)
resolve(2)
reject('error')
}).then((value) => {
console.log(value)
}, (error) => {
console.log('error')
})
Ответ: 1
Потому что Promise
может изменить свое состояние лишь один раз, поэтому отработает любой первый вызов, что и будет результатом
3 Promise then callbacks (opens in a new tab)
Promise.resolve(1)
.then(() => 2)
.then(3)
.then((value) => value * 3)
.then(Promise.resolve(4))
.then(console.log)
Ответ: 6
Promise.resolve(1)
создает выполненный промис со значением 1. Следующий обработчикthen()
получит это значение.then(() => 2)
не обращает внимания на переданное значение 1 и возвращает 2. Это 2 не становится цепочкой промисов, но становится выполненным значением для следующего обработчикаthen()
.then(3)
- это несколько иной случай.then()
ожидает функцию в качестве аргумента. Здесь 3 не является функцией, поэтому данный обработчикthen()
фактически ничего не делает. Он просто передает предыдущее выполненное значение, которое равно 2.then((value) => value * 3)
принимает значение 2, переданное из предыдущегоthen()
, умножает его на 3, получая 6, и передает это 6 следующему обработчикуthen()
.then(Promise.resolve(4))
подобноthen(3)
, это еще один случай, когдаthen()
получает нефункциональный аргумент (на этот раз это выполненный промис, а не прямое значение, как 3). Таким образом, он также ничего не делает и просто передает предыдущее выполненное значение 6.- Наконец,
then(console.log)
записывает переданное значение, которое равно 6.
Block scope 1 (opens in a new tab)
for (var i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 0)
}
for (let i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 0)
}
Ответ: 5555501234
Increment operator (opens in a new tab)
let a = 1
const b = ++a
const c = a++
console.log(a)
console.log(b)
console.log(c)
Ответ: 322
let a = 1
объявляет и инициализирует a значением 1.const b = ++a
увеличиваетa
на 1 до присвоения значенияb
. Так что сейчасa = 2
иb = 2
.const c = a++
присваивает текущее значениеa
переменнойc
, а затем увеличиваетa
на1
. Следовательно,c = 2
, аa = 3
.- Финальные значения, выводимые в выражениях
console.log
, — этоa = 3
,b = 2
иc = 2
. В JavaScript++a
(оператор преинкремента) увеличивает значение a до оценки окружающего выражения, в то время какa++
(оператор постинкремента) увеличивает значение a после его оценки.
4 Promise then callbacks II (opens in a new tab)
Promise.resolve(1)
.then((val) => {
console.log(val)
return val + 1
}).then((val) => {
console.log(val)
}).then((val) => {
console.log(val)
return Promise.resolve(3)
.then((val) => {
console.log(val)
})
}).then((val) => {
console.log(val)
return Promise.reject(4)
}).catch((val) => {
console.log(val)
}).finally((val) => {
console.log(val)
return 10
}).then((val) => {
console.log(val)
})
Ответ: 1 2 undefined 3 undefined 4 undefined undefined
Каждый .then()
или .catch()
вочередь создает новый промис, который решается на основании выполнения предыдущего обработчика. Если обработчик возвращает значение, которое не является промисом, этим значением выполняется следующий промис. Если обработчик возвращает промис, следующий промис будет выполнен с результатом этого промиса.
Объясним ваш пример шаг за шагом:
Promise.resolve(1)
: Создается промис, который немедленно решается с значением1
..then((val) => { console.log(val); return val + 1 })
: Обработчик берет значение1
, выводит его в консоль, затем возвращает2
. Поскольку это не промис, следующий промис решается с значением2
..then((val) => { console.log(val) })
: Этот обработчик получает значение 2, выводит его в консоль, затем не возвращает ничего. По умолчанию, если функция не возвращает значение, она возвращаетundefined
, поэтому следующий промис решается со значениемundefined
..then((val) => { console.log(val); return Promise.resolve(3).then((val) => { console.log(val) }) })
: Этот обработчик получаетundefined
, выводит его в консоль, затем создает новый промис, который решается со значением3
. Этот новый промис затем печатает3
в консоль и не возвращает ничего, так что фактически он решается со значениемundefined
..then((val) => { console.log(val); return Promise.reject(4) })
: Этот обработчик получает undefined, выводит его в консоль, затем возвращает новый промис, который немедленно отклоняется со значением4
..catch((val) => { console.log(val) })
: Обработчик ошибок перехватывает отклоненный промис со значением4
, затем выводит его в консоль и по умолчанию возвращаетundefined
..finally((val) => { console.log(val); return 10 })
: Несмотря на название, методfinally
на самом деле не получает никакого значения. Вместо этого он выполняется, независимо от того, был ли предыдущий промис выполнен успешно или отклонен. Вывод в консоль будетundefined
, возвращаемое значение10
будет проигнорировано..then((val) => { console.log(val) })
: Обработчик принимает undefined (посколькуfinally
не изменяет полученное ранее значение).
6 Arrow Function (opens in a new tab)
const obj = {
dev: 'bfe',
a: function() {
return this.dev
},
b() {
return this.dev
},
c: () => {
return this.dev
},
d: function() {
return (() => {
return this.dev
})()
},
e: function() {
return this.b()
},
f: function() {
return this.b
},
g: function() {
return this.c()
},
h: function() {
return this.c
},
i: function() {
return () => {
return this.dev
}
}
}
console.log(obj.a())
console.log(obj.b())
console.log(obj.c())
console.log(obj.d())
console.log(obj.e())
console.log(obj.f()())
console.log(obj.g())
console.log(obj.h()())
console.log(obj.i()())
Ответ: bfe, bfe, undefined, bfe, bfe, undefined, undefined, undefined, bfe
obj.a():
this
внутри методаa
ссылается наobj
. Значениеthis.dev
будет'bfe'
.obj.b():
this
внутри методаb
(как и вa()
) ссылается наobj
. Значениеthis.dev
будет'bfe'
.obj.c():
this
внутри стрелочной функции указывает на контекст, в котором функция была объявлена. В глобальном контекстеthis.dev
не определено, поэтому результат будетundefined
.obj.d():
this
внутри возвращаемой стрелочной функции будет ссылаться на этот же контекст, что иthis
внутри функцииd
, то есть наobj
. Поэтомуthis.dev
вернет'bfe'
.obj.e():
this.b()
вызывается в контекстеobj
, поэтомуthis.dev
внутриb
будет'bfe'
.obj.f()():
this.b
внутриf
возвращает ссылку на функциюb
, но контекстthis
функцииobj.f
потерян при вызове: результат выполненияobj.f()
вызывается как функция с обычной нотацией вызова функции, а не как метод. Из-за этогоthis
внутриb
, когда он вызывается, не ссылается наobj
, а ссылается на глобальный контекст, в которомdev
не определён. Поэтому результат будетundefined
.obj.g():
this.c()
вызывается внутриg
, гдеthis
указывает наobj
. Однако, внутриc
,this
снова указывает на глобальный контекст, так как c объявлена как стрелочная функция.this.dev
в глобальном контексте не определено, поэтому результат будетundefined
.obj.h()():
this.c
возвращает ссылку на функциюc
, иthis
потерян также, как и вobj.f()()
. Поэтомуthis
внутриc
, когда он вызывается, не ссылается наobj
, а ссылается на глобальный контекст. Поэтому будет возвращеноundefined
.obj.i()():
this
внутри функцииi
ссылается наobj
, аthis
внутри возвращаемой стрелочной функции будет указывать на этот же контекст, что иthis
внутриi
, то есть наobj
. Поэтомуthis.dev
внутри возвращаемой функции будет'bfe'
.