web之消息队列

https://blog.51cto.com/kaimo313/5588246

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/EventLoop

循环模型

JavaScript 有一个基于事件循环的并发模型,事件循环负责执行代码、收集和处理事件以及执行队列中的子任务

函数调用形成了一个由若干帧组成的栈。

1
2
3
4
5
6
7
8
9
10
11
function foo(b) {
let a = 10;
return a + b + 11;
}

function bar(x) {
let y = 3;
return foo(x * y);
}

console.log(bar(7)); /

当调用 bar 时,第一个帧被创建并压入栈中,帧中包含了 bar 的参数和局部变量。当 bar 调用 foo 时,第二个帧被创建并被压入栈中,放在第一个帧之上,帧中包含 foo 的参数和局部变量。当 foo 执行完毕然后返回时,第二个帧就被弹出栈(剩下 bar 函数的调用帧)。当 bar 也执行完毕然后返回时,第一个帧也被弹出,栈就被清空了

对象被分配在堆中,堆是一个用来表示一大块(通常是非结构化的)内存区域的计算机术语

队列

一个 JavaScript 运行时包含了一个待处理消息的消息队列。每一个消息都关联着一个用以处理这个消息的回调函数
运行时会从最先进入队列的消息开始处理队列中的消息。被处理的消息会被移出队列,并作为输入参数来调用与之关联的函数。正如前面所提到的,调用一个函数总是会为其创造一个新的栈帧。

函数的处理会一直进行到执行栈再次为空为止;然后事件循环将会处理队列中的下一个消息(如果还有的话)。

每一个消息完整地执行后,其他消息才会被执行。

  • 优秀的特性:
    当一个函数执行时,它不会被抢占,只有在它运行完毕之后才会去运行任何其他的代码,才能修改这个函数操作的数据
  • 缺点:
    在于当一个消息需要太长时间才能处理完毕时,Web 应用程序就无法处理与用户的交互,例如点击或滚动

添加消息

在浏览器里,每当一个事件发生并且有一个事件监听器绑定在该事件上时,一个消息就会被添加进消息队列。如果没有事件监听器,这个事件将会丢失

多个运行时互相通信

一个 web worker 或者一个跨域的 iframe 都有自己的栈、堆和消息队列。两个不同的运行时只能通过 postMessage 方法进行通信。如果另一个运行时侦听 message 事件,则此方法会向该运行时添加消息。

渲染主线程做哪些事情(单线程)

  • 解析html
  • 解析css
  • 计算样式
  • 布局
  • 处理图层
  • 没秒页面刷新 60次
  • 执行全局js代码
  • 执行事件处理函数
  • 执行定时器的回调函数

模型的迭代

  • 单消息队列

在单消息队列架构下,存在着低优先级任务会阻塞高优先级任务的情况,这个问题称为消息队列的队头阻塞问题。

  • 高优先级队列

在渲染进程中引入一个任务调度器,负责从多个消息队列中选出合适的任务,通常实现的逻辑,先按照顺序从高优先级队列中取出任务,如果高优先级的队列为空,那么再按照顺序从低优级队列中取出任务。

缺点:任务的相对执行顺序会被打乱。

  • 根据消息类型来实现消息队列

不同类型的任务创建不同优先级的消息队列:

  1. 可以创建输入事件的消息队列,用来存放输入事件。
  2. 可以创建合成任务的消息队列,用来存放合成事件。
  3. 可以创建默认消息队列,用来保存如资源加载的事件和定时器回调等事件。
  4. 还可以创建一个空闲消息队列,用来存放 V8 的垃圾自动垃圾回收这一类实时性不高的事件。
    根据消息类型实现不同优先级的消息队列:

缺点:消息队列的优先级都是固定的,任务调度器会按照这种固定好的静态的优先级来分别调度任务。使用静态优先级策略,网页的加载速度会被拖慢。

  • 动态调度

Chromium 当前所采用的任务调度策略:动态调度策略

  • 任务饿死

某个状态下,一直有新的高优先级的任务加入到队列中,这样就会导致其他低优先级的任务得不到执行,这称为任务饿死。

Chromium 为了解决任务饿死的问题,给每个队列设置了执行权重,也就是如果连续执行了一定个数的高优先级的任务,那么中间会执行一次低优先级的任务,这样就缓解了任务饿死的情况。

源码

消息循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
namespace base {

MessagePumpDefault::MessagePumpDefault()
: keep_running_(true),
event_(WaitableEvent::ResetPolicy::AUTOMATIC,
WaitableEvent::InitialState::NOT_SIGNALED) {
event_.declare_only_used_while_idle();
}

MessagePumpDefault::~MessagePumpDefault() = default;

void MessagePumpDefault::Run(Delegate* delegate) {
AutoReset<bool> auto_reset_keep_running(&keep_running_, true);

for (;;) {
#if BUILDFLAG(IS_APPLE)
mac::ScopedNSAutoreleasePool autorelease_pool;
#endif

Delegate::NextWorkInfo next_work_info = delegate->DoWork();
bool has_more_immediate_work = next_work_info.is_immediate();
if (!keep_running_)
break;

if (has_more_immediate_work)
continue;

has_more_immediate_work = delegate->DoIdleWork();
if (!keep_running_)
break;

if (has_more_immediate_work)
continue;

if (next_work_info.delayed_run_time.is_max()) {
event_.Wait();
} else {
event_.TimedWait(next_work_info.remaining_delay());
}
// Since event_ is auto-reset, we don't need to do anything special here
// other than service each delegate method.
}
}

void MessagePumpDefault::Quit() {
keep_running_ = false;
}

void MessagePumpDefault::ScheduleWork() {
// Since this can be called on any thread, we need to ensure that our Run
// loop wakes up.
event_.Signal();
}

void MessagePumpDefault::ScheduleDelayedWork(
const Delegate::NextWorkInfo& next_work_info) {
// Since this is always called from the same thread as Run(), there is nothing
// to do as the loop is already running. It will wait in Run() with the
// correct timeout when it's out of immediate tasks.
// TODO(gab): Consider removing ScheduleDelayedWork() when all pumps function
// this way (bit.ly/merge-message-pump-do-work).
}

} //

队列类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
#ifndef THIRD_PARTY_BLINK_PUBLIC_PLATFORM_TASK_TYPE_H_
#define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_TASK_TYPE_H_

namespace blink {

// A list of task sources known to Blink according to the spec.
// This enum is used for a histogram and it should not be re-numbered.
//
// For the task type usage guideline, see https://bit.ly/2vMAsQ4
//
// When a new task type is created:
// * Set the new task type's value to "Next value"
// * Update kMaxValue to point to the new task type
// * Increment "Next value"
// * in tools/metrics/histograms/enums.xml update the
// "RendererSchedulerTaskType" enum
// * update TaskTypes.md
//
// Next value: 83
enum class TaskType : unsigned char {
///////////////////////////////////////
// Speced tasks should use one of the following task types
///////////////////////////////////////

// Speced tasks and related internal tasks should be posted to one of
// the following task runners. These task runners may be throttled.

// This value is used as a default value in cases where TaskType
// isn't supported yet. Don't use outside platform/scheduler code.
kDeprecatedNone = 0,

// https://html.spec.whatwg.org/multipage/webappapis.html#generic-task-sources
//
// This task source is used for features that react to DOM manipulations, such
// as things that happen in a non-blocking fashion when an element is inserted
// into the document.
kDOMManipulation = 1,
// This task source is used for features that react to user interaction, for
// example keyboard or mouse input. Events sent in response to user input
// (e.g. click events) must be fired using tasks queued with the user
// interaction task source.
kUserInteraction = 2,
// TODO(altimin) Fix the networking task source related namings once it is
// clear how
// all loading tasks are annotated.
// This task source is used for features that trigger in response to network
// activity.
kNetworking = 3,
// This is a part of Networking task that should not be frozen when a page is
// frozen.
kNetworkingUnfreezable = 75,
// This task source is used for control messages between kNetworking tasks.
kNetworkingControl = 4,
// Tasks used to run low priority scripts.
kLowPriorityScriptExecution = 81,
// This task source is used to queue calls to history.back() and similar APIs.
kHistoryTraversal = 5,

// https://html.spec.whatwg.org/multipage/embedded-content.html#the-embed-element
// This task source is used for the embed element setup steps.
kEmbed = 6,

// https://html.spec.whatwg.org/multipage/embedded-content.html#media-elements
// This task source is used for all tasks queued in the [Media elements]
// section and subsections of the spec unless explicitly specified otherwise.
kMediaElementEvent = 7,

// https://html.spec.whatwg.org/multipage/scripting.html#the-canvas-element
// This task source is used to invoke the result callback of
// HTMLCanvasElement.toBlob().
kCanvasBlobSerialization = 8,

// https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model
// This task source is used when an algorithm requires a microtask to be
// queued.
kMicrotask = 9,

// https://html.spec.whatwg.org/multipage/webappapis.html#timers
// For tasks queued by setTimeout() or setInterval().
//
// Task nesting level is < 5 and timeout is zero.
kJavascriptTimerImmediate = 72,
// Task nesting level is < 5 and timeout is > 0.
kJavascriptTimerDelayedLowNesting = 73,
// Task nesting level is >= 5.
kJavascriptTimerDelayedHighNesting = 10,
// Note: The timeout is increased to be at least 4ms when the task nesting
// level is >= 5. Therefore, the timeout is necessarily > 0 for
// kJavascriptTimerDelayedHighNesting.

// https://html.spec.whatwg.org/multipage/comms.html#sse-processing-model
// This task source is used for any tasks that are queued by EventSource
// objects.
kRemoteEvent = 11,

// https://html.spec.whatwg.org/multipage/comms.html#feedback-from-the-protocol
// The task source for all tasks queued in the [WebSocket] section of the
// spec.
kWebSocket = 12,

// https://html.spec.whatwg.org/multipage/comms.html#web-messaging
// This task source is used for the tasks in cross-document messaging.
kPostedMessage = 13,

// https://html.spec.whatwg.org/multipage/comms.html#message-ports
kUnshippedPortMessage = 14,

// https://www.w3.org/TR/FileAPI/#blobreader-task-source
// This task source is used for all tasks queued in the FileAPI spec to read
// byte sequences associated with Blob and File objects.
kFileReading = 15,

// https://www.w3.org/TR/IndexedDB/#request-api
kDatabaseAccess = 16,

// https://w3c.github.io/presentation-api/#common-idioms
// This task source is used for all tasks in the Presentation API spec.
kPresentation = 17,

// https://www.w3.org/TR/2016/WD-generic-sensor-20160830/#sensor-task-source
// This task source is used for all tasks in the Sensor API spec.
kSensor = 18,

// https://w3c.github.io/performance-timeline/#performance-timeline
kPerformanceTimeline = 19,

// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15
// This task source is used for all tasks in the WebGL spec.
kWebGL = 20,

// https://www.w3.org/TR/requestidlecallback/#start-an-event-loop-s-idle-period
kIdleTask = 21,

// Use MiscPlatformAPI for a task that is defined in the spec but is not yet
// associated with any specific task runner in the spec. MiscPlatformAPI is
// not encouraged for stable and matured APIs. The spec should define the task
// runner explicitly.
// The task runner may be throttled.
kMiscPlatformAPI = 22,

// Tasks used for DedicatedWorker's requestAnimationFrame.
kWorkerAnimation = 51,

// Obsolete.
// kNetworkingWithURLLoaderAnnotation = 50, (see crbug.com/860545)
// kExperimentalWebSchedulingUserInteraction = 53,
// kExperimentalWebSchedulingBestEffort = 54,

// https://drafts.csswg.org/css-font-loading/#task-source
kFontLoading = 56,

// https://w3c.github.io/manifest/#dfn-application-life-cycle-task-source
kApplicationLifeCycle = 57,

// https://wicg.github.io/background-fetch/#infrastructure
kBackgroundFetch = 58,

// https://www.w3.org/TR/permissions/
kPermission = 59,

// https://w3c.github.io/ServiceWorker/#dfn-client-message-queue
kServiceWorkerClientMessage = 60,

// https://w3c.github.io/web-locks/#web-locks-tasks-source
kWebLocks = 66,

// Task type used for the Prioritized Task Scheduling API
// (https://wicg.github.io/scheduling-apis/#the-posted-task-task-source).
// This task type should not be passed directly to
// FrameScheduler::GetTaskRunner(); it is used indirectly by
// WebSchedulingTaskQueues.
kWebSchedulingPostedTask = 67,

// https://w3c.github.io/screen-wake-lock/#dfn-screen-wake-lock-task-source
kWakeLock = 76,

// https://storage.spec.whatwg.org/#storage-task-source
kStorage = 82,

///////////////////////////////////////
// Not-speced tasks should use one of the following task types
///////////////////////////////////////

// The default task type. The task may be throttled or paused.
kInternalDefault = 23,

// Tasks used for all tasks associated with loading page content.
kInternalLoading = 24,

// Tasks for tests or mock objects.
kInternalTest = 26,

// Tasks that are posting back the result from the WebCrypto task runner to
// the Blink thread that initiated the call and holds the Promise. Tasks with
// this type are posted by:
// * //components/webcrypto
kInternalWebCrypto = 27,

// Tasks to execute media-related things like logging or playback. Tasks with
// this type are mainly posted by:
// * //content/renderer/media
// * //media
kInternalMedia = 29,

// Tasks to execute things for real-time media processing like recording. If a
// task touches MediaStreamTracks, associated sources/sinks, and Web Audio,
// this task type should be used.
// Tasks with this type are mainly posted by:
// * //content/renderer/media
// * //media
// * blink/renderer/modules/webaudio
// * blink/public/platform/audio
kInternalMediaRealTime = 30,

// Tasks related to user interaction like clicking or inputting texts.
kInternalUserInteraction = 32,

// Tasks related to the inspector.
kInternalInspector = 33,

// Obsolete.
// kInternalWorker = 36,

// Translation task that freezes when the frame is not visible.
kInternalTranslation = 55,

// Tasks used at IntersectionObserver.
kInternalIntersectionObserver = 44,

// Task used for ContentCapture.
kInternalContentCapture = 61,

// Navigation tasks and tasks which have to run in order with them, including
// legacy IPCs and channel associated interfaces.
// Note that the ordering between tasks related to different frames is not
// always guaranteed - tasks belonging to different frames can be reordered
// when one of the frames is frozen.
// Note: all AssociatedRemotes/AssociatedReceivers should use this task type.
kInternalNavigationAssociated = 63,

// Tasks which should run when the frame is frozen, but otherwise should run
// in order with other legacy IPC and channel-associated interfaces.
// Only tasks related to unfreezing itself should run here, the majority of
// the tasks
// should use kInternalNavigationAssociated instead.
kInternalNavigationAssociatedUnfreezable = 64,

// Task used to split a script loading task for cooperative scheduling
kInternalContinueScriptLoading = 65,

// Tasks used to control frame lifecycle - they should run even when the frame
// is frozen.
kInternalFrameLifecycleControl = 68,

// Tasks used for find-in-page.
kInternalFindInPage = 70,

// Tasks that come in on the HighPriorityLocalFrame interface.
kInternalHighPriorityLocalFrame = 71,

// Tasks that are should use input priority task queue/runner.
kInternalInputBlocking = 77,

// Tasks related to the WebGPU API
kWebGPU = 78,

// Cross-process PostMessage IPCs that are deferred in the current task.
kInternalPostMessageForwarding = 79,

// Tasks related to renderer-initiated navigation cancellation.
kInternalNavigationCancellation = 80,

///////////////////////////////////////
// The following task types are only for thread-local queues.
///////////////////////////////////////

// The following task types are internal-use only, escpecially for annotations
// like UMA of per-thread task queues. Do not specify these task types when to
// get a task queue/runner.

kMainThreadTaskQueueV8 = 37,
kMainThreadTaskQueueCompositor = 38,
kMainThreadTaskQueueDefault = 39,
kMainThreadTaskQueueInput = 40,
kMainThreadTaskQueueIdle = 41,
// Removed:
// kMainThreadTaskQueueIPC = 42,
kMainThreadTaskQueueControl = 43,
// Removed:
// kMainThreadTaskQueueCleanup = 52,
kMainThreadTaskQueueMemoryPurge = 62,
kMainThreadTaskQueueNonWaking = 69,
kMainThreadTaskQueueIPCTracking = 74,
kCompositorThreadTaskQueueDefault = 45,
kCompositorThreadTaskQueueInput = 49,
kWorkerThreadTaskQueueDefault = 46,
kWorkerThreadTaskQueueV8 = 47,
kWorkerThreadTaskQueueCompositor = 48,

kMaxValue = kStorage,
};

} // namespace blink

#endif // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_TASK_TYPE_H_