为什么使用函数声明对 web worker 的 onmessage 不起作用?
Why does using a function declaration not work for onmessage with web workers?
TL;DR 是当网络工作者的 onmessage() 处理程序被定义为函数声明而不是函数表达式时,它不起作用。 (至少在 Chrome。)我想不出为什么。
我有以下非常简单的示例设置:
index.html
<!DOCTYPE html>
<html>
<head>
<title>Web worker test</title>
<script src="index.js"></script>
</head>
<body>
<input type="button" value="test1" onclick="run('test1.js')">
<input type="button" value="test2" onclick="run('test2.js')">
</body>
</html>
index.js
function run(filename) {
worker = new Worker(filename);
worker.postMessage("test");
}
test1.js
onmessage = function(msg) {
console.log(msg.data);
}
console.log(self.onmessage);
test2.js
function onmessage(msg) {
console.log(msg.data);
}
console.log(self.onmessage);
您可以在 http://mrbean.cryogenia.com/~davedude/webworkertest/ 上自己尝试。如果您单击 test1 按钮,它会记录以下内容(至少在 Chrome 79.0.3945.117 上):
ƒ (msg) {
console.log(msg.data);
}
test1.js:2 test
这表明函数已按预期定义,处理程序正在运行。
相比之下,当你点击test2时,你得到的是:
ƒ onmessage(msg) {
console.log(msg.data);
}
这表明脚本 正在 工作,并且函数已定义,只是出于某种原因无法作为处理程序运行。
全局对象的onmessage
属性是一个setter。 Setter 不会被函数声明调用;相反,它们只是被覆盖,就好像 delete <fnName>
之前是 运行 一样。这是另一个例子:
<script>
Object.defineProperty(window, 'fn', {
set(newVal) {
console.log('setter invoked');
},
configurable: true
});
</script>
<script>
function fn() {
}
console.log(window.fn);
</script>
如您所见,setter 没有被调用。 fn
属性 在第一个 <script>
之后 window
的状态几乎与 message
属性 在 [=] 的状态相同19=] 在网络工作者脚本开始的网络工作者中:
const workerFn = () => {
console.log(Object.getOwnPropertyDescriptor(self, 'onmessage'));
};
const workerFnStr = `(${workerFn})();`;
const blob = new Blob([workerFnStr], { type: 'text/javascript' });
const worker = new Worker(window.URL.createObjectURL(blob));
<div>Look at results in your browser console, not the snippet console</div>
<br><br>
<img src="https://i.stack.imgur.com/8JND8.png">
将函数设置为 message
事件侦听器的功能只会在 setter 内部发生。但是如果不调用 setter,则不会附加侦听器。
如果你很好奇,in the specification,当顶层的函数声明存在,并且该名称已经存在于全局对象中时,该名称将不会被重新创建或重新初始化,直到, 最后:
Perform ! varEnvRec.SetMutableBinding(fn, fo, false)
.
这将引发错误 only if the binding is immutable(例如,如果 属性 不可配置)。但是 onmessage
getter/setter 是 可配置的。
TL;DR 是当网络工作者的 onmessage() 处理程序被定义为函数声明而不是函数表达式时,它不起作用。 (至少在 Chrome。)我想不出为什么。
我有以下非常简单的示例设置:
index.html
<!DOCTYPE html>
<html>
<head>
<title>Web worker test</title>
<script src="index.js"></script>
</head>
<body>
<input type="button" value="test1" onclick="run('test1.js')">
<input type="button" value="test2" onclick="run('test2.js')">
</body>
</html>
index.js
function run(filename) {
worker = new Worker(filename);
worker.postMessage("test");
}
test1.js
onmessage = function(msg) {
console.log(msg.data);
}
console.log(self.onmessage);
test2.js
function onmessage(msg) {
console.log(msg.data);
}
console.log(self.onmessage);
您可以在 http://mrbean.cryogenia.com/~davedude/webworkertest/ 上自己尝试。如果您单击 test1 按钮,它会记录以下内容(至少在 Chrome 79.0.3945.117 上):
ƒ (msg) {
console.log(msg.data);
}
test1.js:2 test
这表明函数已按预期定义,处理程序正在运行。
相比之下,当你点击test2时,你得到的是:
ƒ onmessage(msg) {
console.log(msg.data);
}
这表明脚本 正在 工作,并且函数已定义,只是出于某种原因无法作为处理程序运行。
全局对象的onmessage
属性是一个setter。 Setter 不会被函数声明调用;相反,它们只是被覆盖,就好像 delete <fnName>
之前是 运行 一样。这是另一个例子:
<script>
Object.defineProperty(window, 'fn', {
set(newVal) {
console.log('setter invoked');
},
configurable: true
});
</script>
<script>
function fn() {
}
console.log(window.fn);
</script>
如您所见,setter 没有被调用。 fn
属性 在第一个 <script>
之后 window
的状态几乎与 message
属性 在 [=] 的状态相同19=] 在网络工作者脚本开始的网络工作者中:
const workerFn = () => {
console.log(Object.getOwnPropertyDescriptor(self, 'onmessage'));
};
const workerFnStr = `(${workerFn})();`;
const blob = new Blob([workerFnStr], { type: 'text/javascript' });
const worker = new Worker(window.URL.createObjectURL(blob));
<div>Look at results in your browser console, not the snippet console</div>
<br><br>
<img src="https://i.stack.imgur.com/8JND8.png">
将函数设置为 message
事件侦听器的功能只会在 setter 内部发生。但是如果不调用 setter,则不会附加侦听器。
如果你很好奇,in the specification,当顶层的函数声明存在,并且该名称已经存在于全局对象中时,该名称将不会被重新创建或重新初始化,直到, 最后:
Perform !
varEnvRec.SetMutableBinding(fn, fo, false)
.
这将引发错误 only if the binding is immutable(例如,如果 属性 不可配置)。但是 onmessage
getter/setter 是 可配置的。