Вместо того, чтобы бросать код на вас, есть два понятия, которые являются ключом к пониманию того, как JS обрабатывает обратные вызовы и асинхронность. (это даже слово?)
Есть три вещи, о которых вам нужно знать; Очередь; цикл события и стек
. В широких упрощенных терминах цикл событий подобен диспетчеру проекта, он постоянно прослушивает любые функции, которые хотят запускать и взаимодействовать между очереди и стека.
while (queue.waitForMessage()) {
queue.processNextMessage();
}
Как только он получает сообщение для запуска чего-то, он добавляет его в очередь. Очередь - это список вещей, которые ждут выполнения (например, ваш запрос AJAX). Представьте себе это так:
1. call foo.com/api/bar using foobarFunc
2. Go perform an infinite loop
... and so on
Когда одно из этих сообщений будет исполнено, оно выталкивает сообщение из очереди и создает стек, стек - это все, что нужно выполнить JS для выполнения инструкции в сообщение. Таким образом, в нашем примере ему говорят позвонить foobarFunc
function foobarFunc (var) {
console.log(anotherFunction(var));
}
. Так что все, что foobarFunc должно выполнить (в нашем случае anotherFunction
), будет вставлено в стек. исполняемый, а затем забытый - цикл события затем переместится на следующую вещь в очереди (или прослушивает сообщения)
. Главное здесь - порядок выполнения. Это
Когда вы совершаете вызов с использованием AJAX для внешней стороны или выполняете любой асинхронный код (например, setTimeout), Javascript зависит от ответ, прежде чем он сможет продолжить.
Большой вопрос, когда он получит ответ? Ответ в том, что мы не знаем, поэтому цикл событий ждет, когда это сообщение скажет: «Эй, забери меня». Если JS просто ждал этого сообщения синхронно, ваше приложение замерзнет, и оно сосать. Таким образом, JS продолжает выполнение следующего элемента в очереди, ожидая, пока сообщение не будет добавлено обратно в очередь.
Вот почему с асинхронной функциональностью мы используем вещи, называемые обратными вызовами. Это похоже на обещание буквально. Как и в I , обещание что-то вернуть в какой-то момент jQuery использует специальные обратные вызовы, называемые deffered.done
deffered.fail
и deffered.always
(среди других). Вы можете увидеть их все здесь
Итак, вам нужно передать функцию, которая в какой-то момент будет выполнена с переданными ей данными.
Поскольку обратный вызов не выполняется немедленно, но в более позднее время важно передать ссылку на функцию, которую она не выполнила. поэтому
function foo(bla) {
console.log(bla)
}
, поэтому большую часть времени (но не всегда) вы пройдете foo
не foo()
. Надеюсь, это будет иметь смысл. Когда вы сталкиваетесь с такими вещами, которые кажутся запутанными, я настоятельно рекомендую полностью прочитать документацию, чтобы хотя бы понять ее. Это сделает вас намного лучшим разработчиком.
Изучите существующий код, который делает подобные вещи. Например:
SAVE_ARGS_IRQ
): entry_64.S INTR_PUSH
): privregs.h IDT_VEC
): exception.S (аналогично vector.S в NetBSD) Фактически, «ручное нажатие» рег является единственным способом на AMD64, так как PUSHA
там не существует. AMD64 не уникален в этом аспекте - большинство процессоров, отличных от x86, требуют некоторого сохранения / восстановления регистров по регистру.
Но если вы внимательно проверите ссылочный исходный код, вы обнаружите, что не все обработчики прерываний требуют сохранения / восстановления всего набора регистров, поэтому есть возможности для оптимизации.
Привет, возможно, это не правильный способ сделать это, но можно создать макросы, такие как
.macro pushaq
push %rax
push %rcx
push %rdx
push %rbx
push %rbp
push %rsi
push %rdi
.endm # pushaq
и
.macro popaq
pop %rdi
pop %rsi
pop %rbp
pop %rbx
pop %rdx
pop %rcx
pop %rax
.endm # popaq
, и в итоге добавить другие регистры r8-15 если нужно
AMD нуждалась в некоторой возможности для добавления новых кодов операций для префиксов REX
и некоторых других новых инструкций при разработке 64-разрядных расширений x86. Они изменили значение некоторых кодов операций на эти новые инструкции.
Несколько инструкций были просто короткими формами существующих инструкций или в противном случае не были необходимы. PUSHA
была одной из жертв. Непонятно, почему они запретили PUSHA
, хотя, похоже, он не перекрывает никаких новых кодов операций с инструкциями. Возможно, они зарезервированы для кодов PUSHA
и POPA
для будущего использования, поскольку они полностью избыточны и не будут быстрее и не будут встречаться достаточно часто в коде.
Порядок из PUSHA
был порядок кодирования команд: eax
, ecx
, edx
, ebx
, esp
, ebp
, esi
, edi
. Обратите внимание, что он избыточно нажал esp
! Вам нужно знать esp
, чтобы найти данные, которые он нажал!
Если вы конвертируете код из 64-битного кода PUSHA
, все равно, вам нужно его обновить, чтобы нажать новые регистры r8
через r15
. Вам также необходимо сохранить и восстановить гораздо большее состояние SSE, xmm8
через xmm15
. Предполагая, что вы собираетесь их сбивать.
Если код обработчика прерываний - это просто заглушка, которая пересылает код C, вам не нужно сохранять все регистры. Вы можете предположить, что компилятор C будет генерировать код, который будет сохранять rbx
, rbp
, rsi
, rdi
и r12
через r15
. Вам нужно сохранить и восстановить rax
, rcx
, rdx
и r8
через r11
. (Примечание: на Linux или других платформах System V ABI компилятор будет сохранять rbx
, rbp
, r12
- r15
, вы можете ожидать rsi
и rdi
clobbered) .
Регистры сегментов не сохраняют значения в длинном режиме (если прерываемый поток работает в режиме совместимости с 32 битами, вы должны сохранять регистры сегментов, спасибо ughoavgfhw). Фактически, они избавились от большей части сегментации в длинном режиме, но FS
по-прежнему зарезервирован для использования операционными системами в качестве базового адреса для локальных данных потока. Само значение регистра не имеет значения, база FS
и GS
устанавливается через MSR 0xC0000100
и 0xC0000101
. Предполагая, что вы не будете использовать FS
, вам не нужно беспокоиться об этом, просто помните, что любые локальные данные потока, к которым обращается код C, могут использовать TLS любого случайного потока. Будьте осторожны, потому что библиотеки времени выполнения C используют TLS для некоторой функциональности (например: strtok обычно использует TLS).
Загрузка значения в FS
или GS
(даже в пользовательском режиме) перезапишет FSBASE
или GSBASE
MSR. Поскольку некоторые операционные системы используют GS
как «локальное» процессорное хранилище (им нужен способ иметь указатель на структуру для каждого ЦП), им необходимо сохранить его где-то, что не будет сбиваться при загрузке GS
у пользователя Режим. Для решения этой проблемы существуют два MSR, зарезервированных для регистра GSBASE
: один активный и один скрытый. В режиме ядра ядро GSBASE
хранится в обычном GSBASE
MSR, а база пользовательского режима находится в другом (скрытом) GSBASE
MSR. Когда контекст переключается из режима ядра в контекст пользовательского режима, а при сохранении контекста пользовательского режима и входа в режим ядра, код переключения контекста должен выполнять команду SWAPGS, которая меняет значения видимого и скрытого GSBASE
MSR. Поскольку ядро GSBASE
безопасно скрыто в другом режиме MSR в пользовательском режиме, код режима пользователя не может сжимать GSBASE
ядра, загружая значение в GS
. Когда CPU снова загрузит режим ядра, код сохранения контекста выполнит SWAPGS
и восстановит GSBASE
ядра.
pusha
недопустим в 64-битном режиме, поскольку он является избыточным. [1] [1]