WebGL上下文创建错误事件:它是否同步触发?

3

webglcontextcreationerror事件是同步触发还是异步触发的?例如,是否

canvas.addEventListener("webglcontextcreationerror", function() {
  console.log("Error");
});
var context = canvas.getContext("webgl");
console.log("After creation");

在错误输出的情况下
"After creation"
"Error"

或者

"Error"
"After creation"

我不确定如何强制该事件以便自己找到答案。


一个事件不总是异步的吗?有趣的是,当您首先请求2D上下文,然后再请求WebGL上下文时,即使在Firefox上,“用户代理无法创建WebGLRenderingContext上下文”,事件也不会触发。所以我刚刚在Chrome上尝试了一下,它确实同步地触发了事件...但是WebKit存在事件在同步调用流程结束之前触发的错误,因此我不知道它是否可以成为真正而坚实的答案。 - Kaiido
“事件不总是异步的吗?” 我不这么认为...它的触发/分派可以由程序员自行决定。例如,https://plnkr.co/edit/kYrcNjWHFaZ1WYYrgsOY?p=preview 可以同步触发自定义事件。 - Michal Charemza
好的,你是正确的,答案是同步的。你可以在Chrome中使用以下代码进行测试:var canvas = document.createElement('canvas') canvas.addEventListener("webglcontextcreationerror", function() { console.log("Error"); }); canvas.getContext('2d') canvas.getContext("webgl"); console.log("After creation"); - Kaiido
@Kaiido 太好了!在创建 WebGL 上下文之后创建 2D 上下文是一种强制错误的方式。请随意将您的评论添加为答案。 - Michal Charemza
2个回答

2

所以一个问题是为什么你关心顺序?getContext在失败时返回null,所以如果你想知道它是否失败了,那就没事了。 webglcontextcreationerror的唯一目的是让你知道它失败的原因,因为getContext没有办法做到这一点。因此,你可以构造你的代码,使得它不管是同步还是异步都无所谓。

var canvas = document.createElement('canvas')

function doSomethingWithStatusMessage(e) {
  log("context creation error: "+ e.statusMessage);
}

canvas.addEventListener("webglcontextcreationerror", doSomethingWithStatusMessage);

log("2d: " + canvas.getContext('2d'));
log("webgl: " + canvas.getContext("webgl"));
log("after creation");

function log(msg, color) {
  var div = document.createElement("pre");
  div.appendChild(document.createTextNode(msg));
  document.body.appendChild(div);
}

doSomethingWithStatus 消息可以执行任何你想要的操作。假设基于 getContext,你需要显示一个对话框。

if (!canvas.getContext("webgl")) {
   g_dialog = new Dialog("can't create context", g_reason);
}

那么你可能会有类似以下的代码

var g_reason = "unknown";
var g_dialog;

function doSomethingWithStatusMessage(e) {
  if (g_dialog) {
    g_dialog.updateReason(e.statusMessage);
  } else {
    g_reason = g.statusMessage;
  }
}

function Dialog(msg, reason) {
  var div = document.createElement("div");
  div.appendChild(document.createTextNode(msg));
  var reasonNode = document.createTextNode("");
  div.appendChild(reasonNode);
  updateReason(reason);      

  function updateReason(reason) {
    reasonNode.nodeValue = reason;
  }
  this.updateReason = updateReason;
}

var g_reason = "unknown";
var g_dialog;

function doSomethingWithStatusMessage(e) {
  if (g_dialog) {
    g_dialog.updateReason(e.statusMessage);
  } else {
    g_reason = e.statusMessage;
  }
}

var canvas = document.createElement('canvas')

canvas.addEventListener("webglcontextcreationerror", doSomethingWithStatusMessage);

log("2d: " + canvas.getContext('2d'));
var gl = canvas.getContext("webgl");
log("webgl: " + gl);
log("after creation");

if (!gl) {
  new Dialog("could not create WebGL context: ", g_reason);
}

function log(msg, color) {
  var div = document.createElement("pre");
  div.appendChild(document.createTextNode(msg));
  document.body.appendChild(div);
}

function Dialog(msg, reason) {
  var outer = document.createElement("div");
  outer.className = "dialog";
  var div = document.createElement("div");
  div.appendChild(document.createTextNode(msg));
  var reasonNode = document.createTextNode("");
  div.appendChild(reasonNode);
  outer.appendChild(div);
  updateReason(reason);      
  document.body.appendChild(outer);
  outer.addEventListener('click', close);

  function updateReason(reason) {
    reasonNode.nodeValue = reason;
  }
  
  function close() {
    document.body.removeChild(outer);
    outer.removeEventListener('click', close);
  }
  
  this.close = close;
  this.updateReason = updateReason;
}
.dialog {
  position: absolute;
  z-index: 2;
  background: rgba(0,0,0,0.8);
  color: red;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  
  display: flex;
  justify-content: center;
  align-content: center;
  align-items: center;
}

如果您希望将错误报告给服务器,那么只需稍后触发它,以便异步消息有机会到达即可。请保留HTML标签。
if (!canvas.getContext("webgl")) {
   // give a moment for the async message to arrive
   setTimeout(uploadReason, 1000);
}

其余的部分只是HTML标记。
var g_reason = "unknown";

function doSomethingWithStatusMessage(e) {
  g_reason = e.statusMessage;
}

function uploadReason() {
  .. XHR g_reason to server ..
}

注意:我在规范的这一部分提交了一个错误报告,因此如果您愿意,可以跟进。

啊,关于返回 null 的问题,你说得很好。我会在我的代码中考虑这个问题。 - Michal Charemza

0

NB:这不是一个按规范的答案,仅来自单个 UA 测试,因此可能是错误的,或者至少只适用于此 UA,也称为 Chrome。


在Chrome中,要强制出现错误事件,您可以先请求一个2dContext,然后再请求一个WebGL上下文。
这样我们就可以看到这个UA将事件视为同步事件。
我刚刚学习了这些...

var canvas = document.createElement('canvas')

canvas.addEventListener("webglcontextcreationerror", function() {
  snippet.log("Error");
});

canvas.getContext('2d')
canvas.getContext("webgl");

snippet.log("After creation");
<!-- Provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接