$.fn = {
ready: function(callback){
// dont use "interactive" on IE <= 10 (it can fired premature)
//
// document.readyState:当document文档正在加载时,返回"loading"。当文档结束渲染但在加载内嵌资源时,返回"interactive",并引发DOMContentLoaded事件。当文档加载完成时,返回"complete",并引发load事件。
// document.documentElement.doScroll:IE有个特有的方法doScroll可以检测DOM是否加载完成。 当页面未加载完成时,该方法会报错,直到doScroll不再报错时,就代表DOM加载完成了
if (document.readyState === "complete" ||
(document.readyState !== "loading" && !document.documentElement.doScroll))
setTimeout(function(){ callback($) }, 0) // 重点
else {
// 监听移除事件
var handler = function() {
document.removeEventListener("DOMContentLoaded", handler, false)
window.removeEventListener("load", handler, false)
callback($)
}
document.addEventListener("DOMContentLoaded", handler, false)
window.addEventListener("load", handler, false)
}
return this;
},
}
Время установлено на 0, что должно быть выполнено немедленно, так зачем вам нужно устанавливать fn в setTimeout?
1. Нить
1. Ядро браузера является многопоточным, и они взаимодействуют друг с другом под управлением ядра для поддержания синхронизации Браузер обычно состоит из следующих резидентных потоков: поток рендеринга GUI, поток движка javascript, поток триггера событий браузера , поток триггера синхронизации, поток асинхронного HTTP-запроса.
- Поток рендеринга графического интерфейса: Отвечает за отрисовку HTML-элементов интерфейса браузера. Когда интерфейс необходимо перерисовать (Repaint) или вызвать перекомпоновку (reflow) в результате какой-либо операции, этот поток будет выполняться. Пока движок Javascript запускает сценарий, поток рендеринга GUI приостанавливается, то есть «замораживается». То есть поток рендеринга графического интерфейса и механизм JS являются взаимоисключающими.При выполнении механизма JS поток графического интерфейса будет приостановлен, а обновления графического интерфейса будут храниться в очереди и выполняться немедленно, когда механизм JS простаивает.
- поток движка javascript: также известное как ядро JS, в основном отвечающее за обработку программ сценариев Javascript, таких как движок V8. Поток движка Javascript, естественно, отвечает за синтаксический анализ сценария Javascript и выполнение кода. Браузер имеет только один поток JS, выполняющий JS-программы в любой момент времени.
- Нить триггера события браузера: когда событие инициировано, поток добавит событие в конец очереди ожидания, ожидая, пока механизм JS обработает его. Эти события могут быть в настоящее время выполняемыми блоками кода, такими как временные задачи, или другими потоками из ядра браузера, такими как щелчки мыши, асинхронные запросы AJAX и т. д., но из-за однопоточных отношений JS все эти события должны быть поставлены в очередь. для обработки движком JS.
- синхронизированный триггерный поток: Счетчик времени браузера не учитывается движком JavaScript, потому что движок JavaScript однопоточный, если он находится в состоянии заблокированного потока, это повлияет на точность времени, поэтому разумнее использовать отдельный поток. время и запустить время.
- Асинхронный поток HTTP-запросов: после подключения XMLHttpRequest через браузер открывается новый запрос потока.При обнаружении изменения состояния, если установлена функция обратного вызова, асинхронный поток сгенерирует событие изменения состояния и поместит его в очередь обработки JavaScript. двигатель для обработки.
Например, посмотрите, как эти потоки работают вместе:
Пример 1. Асинхронный запрос выполняется потоком выполнения JavaScript, потоком HTTP-запроса и потоком триггера события. Поток выполнения JavaScript выполняет код асинхронного запроса. В это время браузер откроет новый поток HTTP-запроса для выполнения запроса, а поток выполнения JavaScript продолжит выполнение оставшихся задач в очереди выполнения. Затем в какой-то момент в будущем поток, запускающий событие, отслеживает выполнение ранее инициированного HTTP-запроса и вставляет код обратного вызова события завершения в конец очереди выполнения JavaScript, чтобы дождаться потока выполнения JavaScript. простаивать для обработки.
Пример 2. Триггер синхронизации (setTimeout и setInterval) представляет собой счетчик времени, выполняемый потоком таймера браузера, а затем вставляет код выполнения функции обработки времени в конец очереди выполнения JavaScript в конце времени времени (т.е. используйте эти два. Когда функция выполняется, фактическое время выполнения больше или равно указанному времени, и точное время не гарантируется).
2. JavaScript является однопоточным и может выполнять только одну операцию за раз.
Давайте поговорим о стеке вызовов js (стеке вызовов) здесь, вы можете принципиально понять однопоточный процесс выполнения.
Порекомендуйте сайт артефакта:LoupeЕго можно использовать для визуализации процесса стека вызовов, и вы можете запустить пример на веб-сайте, это безумно просто.
js call stack (стек вызовов): При вызове функции она будет добавлена в вершину стека вызовов.После завершения выполнения функция будет удалена из вершины стека вызовов.Ключ к этим данным структура «последний пришел — первый ушел», то есть ЛИФО (последним пришел — первым ушел).
Например:
от (Модель параллелизма и цикл событий)
function f(b) {
var a = 12;
return a + b + 35;
}
function g(x) {
var m = 4;
return f(m * x);
}
g(21);
При вызове функции g создается первый кадр кучи (Heap) стека (stack), который содержит параметры g и локальные переменные. Когда g вызывает f, второй кадр стека создается и помещается поверх первого кадра стека, содержащий параметры f и локальные переменные. Когда f возвращает значение, самый верхний кадр стека извлекается из стека (оставляя кадр стека для вызова функции g). Когда g возвращается, стек пуст.
Другой пример:
function test() {
setTimeout(function() {
alert(1)
},1000);
alert(2);
}
test();
При выполнении функционального теста test сначала помещается в стек.Если setTimeout не добавлен в alert(1), тогда alert(1) помещается в стек вторым, а alert(2) последним. Но теперь, после добавления setTimeout в alert(1), alert(1) добавляется в новый стек для ожидания и выполняется через 1 с, поэтому фактическим результатом выполнения является сначала alert(2), а затем alert(1).
3, Очередь задачи (очередь сообщений):
- Существует два типа функций: синхронные и асинхронные.
Синхронная функция: если вызывающая сторона может получить ожидаемый результат (то есть получить ожидаемое возвращаемое значение или увидеть ожидаемый эффект) при возврате функции A, то функция является синхронной.
пример:
console.log('Hi’); //函数返回时,就看到了预期的效果:在控制台打印了一个字符串
Асинхронная функция: то есть, если вызывающая сторона не может получить ожидаемый результат при возврате функции А, но должна получить его определенными средствами в будущем, то эта функция является асинхронной.
пример:
setTimeout(fn, 1000);//setTimeout是异步过程的发起函数,fn是回调函数。
- Также есть два типа задач: синхронные задачи и асинхронные задачи.
Синхронные задачи: задачи, поставленные в очередь на выполнение в основном потоке, и последняя задача может быть выполнена только после завершения выполнения первой задачи.
Асинхронная задача: основной поток инициирует асинхронный запрос (то есть выполняет асинхронную функцию), а соответствующий рабочий поток (поток триггера события браузера, поток асинхронного HTTP-запроса и т. д.) получает запрос и сообщает основному потоку, что он был получен (асинхронная функция возвращает); основной поток можно продолжать выполнять следующий код, в то время как рабочий поток выполняет асинхронные задачи; после завершения работы рабочий поток помещает сообщение о завершении в очередь задач (сообщений), и основной поток получает задачу (сообщение) через процесс цикла обработки событий, а затем выполняет определенные действия (вызов функции обратного вызова).
На рисунке основной поток — Stack, а очередь задач — Queue.
- Очередь задач. Очередь задач (сообщений) — это очередь в порядке очереди, в которой хранятся различные задачи (сообщения).
- Цикл событий. Цикл событий относится к процессу, в котором основной поток неоднократно извлекает и выполняет задачи (сообщения) из очереди задач (сообщений). Процесс получения задачи (сообщения) и ее выполнения называется циклом.
Причина слова event в цикле событий: каждое сообщение в очереди задач (сообщений) фактически соответствует событию — событию dom.
пример:
var button = document.getElement('#btn');
button.addEventListener('click',function(e) {
console.log(1);
});
С точки зрения асинхронного процесса функция addEventListener является инициирующей функцией асинхронного процесса, а функция прослушивателя событий — функцией обратного вызова асинхронного процесса. Когда событие запускается, это означает, что асинхронная задача завершена, а функция прослушивателя событий инкапсулируется в сообщение и помещается в очередь сообщений, ожидая выполнения основного потока.
Так что же такое задача (сообщение)? Задача (сообщение) — это функция обратного вызова, добавляемая при регистрации асинхронной задачи. Если асинхронная функция не имеет обратного вызова, она не будет помещена в очередь задач (сообщений).
Подводя итог процессу: после того, как основной поток выполнит весь код в текущем цикле, он перейдет в очередь задач (сообщений), чтобы получить сообщение и выполнить его. На этом уведомление рабочего потока основному потоку завершено, и функция обратного вызова выполнена. Если основной поток не предоставляет функцию обратного вызова в начале, рабочему потоку не нужно уведомлять основной поток, и, следовательно, нет необходимости помещать сообщения в очередь сообщений.
Пример: рабочий поток — это поток асинхронного HTTP-запроса, то есть поток Ajax.
Наконец, обратите внимание, что функция обратного вызова асинхронного процесса не должна выполняться в текущем раунде цикла обработки событий. Но когда этот раунд выполнения завершается, основной поток оказывается пустым, и тогда он берется из очереди задач (сообщений).
Посмотрите еще раз на эту картинку
При запуске основного потока генерируется куча и стек, код в стеке вызывает различные внешние API, и они добавляют различные события (щелчок, загрузка, выполнение) в «очередь задач». Пока код в стеке выполняется, основной поток будет читать «очередь задач» и по очереди выполнять функции обратного вызова, соответствующие этим событиям.
3. Роль setTimeout(fn, 0)
Вызов функции setTimeout добавляет сообщение в очередь по истечении периода времени. Этот период времени передается в качестве второго параметра функции. Если в очереди нет других сообщений, сообщение будет обработано немедленно. Однако при наличии других сообщений сообщение setTimeout должно дождаться завершения обработки других сообщений. Таким образом, второй параметр представляет только минимальное время, а не точное время.
Нулевая задержка не означает, что обратный вызов будет выполнен немедленно. При вызове setTimeout с нулевой задержкой функция обратного вызова не выполняется сразу после истечения заданного интервала времени. Время ожидания зависит от количества сообщений, ожидающих в очереди. Другими словами, setTimeout() просто вставляет событие в очередь задач, и основной поток должен дождаться выполнения текущего кода (стека выполнения) перед выполнением указанной им функции обратного вызова. Если текущий код занимает много времени, это может занять много времени, поэтому нет способа гарантировать, что функция обратного вызова будет выполнена во время, указанное setTimeout().
пример
setTimeout(function() {
console.log(1);
},0);
console.log(2)
Выполнить результат 2, 1. Потому что только после того, как вторая строка будет выполнена и основной поток станет пустым, он перейдет в очередь задач, чтобы получить задачу и выполнить функцию обратного вызова.
Описание: Смысл setTimeout(fn,0) в том, чтобы указать, что задача выполняется в самое раннее доступное время простоя основного потока, то есть как можно раньше. Он добавляет событие в конец «очереди задач», поэтому оно не будет выполняться до тех пор, пока основной поток не обработает как задачу синхронизации, так и существующие события в «очереди задач».
В некоторой степени мы можем использовать функцию setTimeout(fn,0) для корректировки порядка задач браузера.
catch me:
Знаю почти:Ли Цзяи
Знать столбец:Бумажная паста Ли Цзяи