javascript强制不同步

时间:2017-02-15 23:19:50

标签: javascript google-chrome

我在cgi中有一个非常简单的js函数,用于12年以上的事情。

目的是通过自定义URL处理程序在客户端pc上启动程序。 (运行命令行工具从文档扫描程序执行TWAIN扫描,然后将pdf发回到同一服务器上的相同cgi。)

<script>
function tsu(e,v) {
  t = window.open("tsu://;${HTTP_HOST};"+e,"_blank");
  t.close();
  window.location.replace("tsu?"+v);
}
</script>
[...]
<input type="button" value="Scan" onClick="tsu('$TSU_QS','$TSU_VIEW_QS')">

$ HTTP_HOST,$ TSU_QS和$ TSU_VIEW_QS是生成此输出的ksh脚本cgi中的shell变量。

最终的window.location.replace()不需要处于同步顺序。这将重新加载此代码出现的相同cgi(ksh脚本),并带有一个新的查询字符串,告诉它等待预期的新pdf出现在服务器文件系统上,或者在完成加载之前超时,以便重新绘制页面现在包括新扫描和上传文件的缩略图和链接。

只有在open()启动外部处理程序之前才需要阻塞close()。即使只需要启动它,也不知道它何时完成。这是在重新加载当前窗口期间处理的。

现在,自2016-10-21以来,当Chrome将函数的默认行为更改为异步时,这不再有效。 close()发生得太快,外部处理程序应用程序永远不会发生。

最初,我尝试改为:

function tsu(e,v) {
  t = window.open("tsu://;${HTTP_HOST};"+e,"_blank");
  if (navigator.userAgent.toLowerCase().indexOf('chrom')>-1) {
    t.addEventListener('load',t.close());
  } else t.close();
  window.location.replace("tsu?"+v);
}    

它在某些情况下起作用而在其他情况下起作用。它使Windows机器工作,但仍然无法在OSX机器上工作。

通过简单地注释掉关闭来调试/验证,然后运行外部应用程序,但是我有一个我不想要的空白选项卡。

(我已经知道通过名称检测铬和铬不是长期答案,因为最终所有浏览器都会有相同的行为,但目前的问题是,Edge具有addeventlistener功能,但它不起作用是的,所以我不能简单地通过该功能的存在。)

所以这有三个问题:

1) 我无法弄清楚如何使用这个已经在每个浏览器中工作超过12年的简单3行javascript,并将其转换为使用新的异步/等待事物。

2) 我也无法弄清楚如何检测甚至可以尝试使用“等待”(如果这甚至是我需要的),因为“typeof await”表示“未定义”即使在当前的Chrome上展示了新的“ async-by-default“问题。该功能似乎是新功能,因此尝试在旧浏览器上使用它是一个错误,但是,我发现有关async / await的所有页面都没有说明如何检测何时合法使用它以及何时使用它。

3) 也许有一种方法可以省去整个tsu()函数,只需要为tsu:// ...提供一个简单的链接或按钮,就像mailto:link一样。并检测是否有其他方式点击重新加载当前页面? 然后我甚至不关心async / await。

[UPDATE] 到目前为止,从答案中加入了一些内容:

  • 仅仅是为了测试,我现在删除了window.location.replace,因此在错误的时间替换当前窗口没有任何问题。

  • 没有浏览器测试或功能测试,每次都做同样的事情

  • 在addeventlistener的参数中使用了函数(){...}

因此,使用下面的代码,新选项卡打开,外部处理程序应用程序运行,新窗口永远不会关闭。控制台显示“尝试addEventListener”,但从不显示“获取事件”。这适用于OSX上的Chrome和Safari。

所以在这个版本中,事件监听器无法正常工作。

function tsu(e,v) {
  t = window.open("tsu:;${HTTP_HOST};"+e,"_blank");
  console.log("trying addEventListener");
  t.addEventListener('load',function() {
    console.log("got event");
    t.close();
  });
  //window.location.replace("tsu?"+v);
}

[更新2] 我仍然没有找到一种方法让这个功能在2016年末Chrome改变之前就一直运行起来,但是我找到了一种不同的方式来获得我需要的东西,而不使用这个功能或根本不需要它。回答如下。

3 个答案:

答案 0 :(得分:0)

两件事:

  1. 如果您打开的URL位于不同的Origin上,您将无法向window.open返回值添加事件侦听器,这看起来就是这种情况 - 我看到您已切换tsu://as://。如果你想继续这种方法,两个页面都必须在同一个Origin上。

  2. async / await是Promises之上的抽象。我不认为这是你问题的最佳解决方案,所以我不会实现它 - 但如果你想采用这种方法,你的负载监听器必须解决这个问题。

  3. 你最好的选择是一个简单的黑客 - 只需等待一秒钟即可关闭窗口。

    var t = window.open("as://;${HTTP_HOST};"+e,"_blank");
    
    setTimeout(function() {
      t.close();
    }, 1000);
    

答案 1 :(得分:0)

让我们看看你的尝试:

function tsu(e,v) {
  t = window.open("tsu://;${HTTP_HOST};"+e,"_blank");
  if (navigator.userAgent.toLowerCase().indexOf('chrom')>-1) {
    t.addEventListener('load',t.close());
  } else t.close();
  window.location.replace("/cgi-bin/tsu?"+v);
}    

第一个显而易见的事情是您没有订阅该活动,而是立即致电t.close()

其次,为什么要尝试检测浏览器是否为chrome?订阅事件监听器是正确的方法,它应该适用于每个浏览器。如果它不能在某些浏览器上运行,那就是您想要解决的浏览器,而不是其他浏览器。

所以你的功能可能就是:

function tsu(e,v) {
  t = window.open("tsu://;${HTTP_HOST};"+e,"_blank");
  t.addEventListener('load',function() {
      t.close();
      // you need location replace here else the page could be redirected before the popup gets closed
      window.location.replace("/cgi-bin/tsu?"+v);
  });
}    

经过一番思考后,如果您可以在加载后将弹出窗口编程为自动关闭,那会更好。

答案 2 :(得分:0)

我自己已经回答了。好吧,我找到了一个不同的答案来完成我的最终任务,但它没有回答“我怎么能让这3行今天连续执行,就像他们这么多年来一直执行的那样2016?”。

我刚刚发现,如果我完全放弃javascript并只使用普通表单输入按钮,我会得到我真正想要的大部分行为。

tsu:url启动。 注册的外部处理程序应用程序在客户端上运行 浏览器中显示的当前页面不会更改。 没有新窗口或标签可以担心关闭。

<form action="tsu://;host;string"><input type="submit" value="Scan"></form>

没有tsu(),没有onClick。

问题就在于,如果我使用这种方法,那么每当按下该按钮时,如何重新加载当前页面(使用我之前在cgi中生成的不同查询字符串)?

我尝试添加onClick只是为了运行window.location.replace()而不是tsu(),就像这样,但它没有改变当前页面。外部应用程序已启动,因此action = ...工作,但当前页面没有重新加载,因此onClick没有(似乎)工作。

<form action="tsu://;host;string"><input type="submit" value="Scan" onClick="window.location.replace('/index.html')"></form>

替代:

<form action="tsu://;host;string"><button onClick="window.location.replace('/index.html')">Scan</button></form>

但是如果我用alert()替换window.location.replace(),我就会得到警告,并且在action =“...”之前首先发生警告。

奇怪,但两个动作都发生了DID,所以我只是尝试用setTimeout()替换alert()(在里面重新加载)并且瞧,最终让我回到了Chrome已经决定打破12之前的位置几年......好吧,但是说真的,我不能说够了......

所以,最后的结论,这是有效的。

action = ...首先触发外部处理程序应用程序。 然后一瞬间,当前浏览器页面重新加载到指定的新URL(实际上与新查询字符串实际上是相同的URL)。

<form action="tsu://;host;string"><button onClick="setTimeout(function(){window.location.replace('other_url.html');},200)">Scan</button></form>

为了完整起见,将这些虚拟值替换为问题顶部的原始发布代码到这种新形式:

<form action="tsu://;${HTTP_HOST};${TSU_QS}","><button onClick="setTimeout(function(){window.location.replace('tsu?${TSU_VIEW_QS}');},200)">Scan</button></form>

最后,我认为这是比我一直做的更好的方式来做我正在做的事情。不再打开,然后立即关闭一个新窗口。当用户开始使用它时,我会观察并看看我是否需要将200高达1000左右。

感谢大家的建议和信息。