Shared Event-loop for Same-Origin Windows(译)
一 前言在翻译这篇文章Tasks,microtasks,queues and schedules时,有一个不懂之处:"All windows on the same origin share an event loop as they can synchronously communicate."Google之后就有了这篇文章。 我们将一组用相互用script连接的Tabs称为一个浏览实例,它对应于HTML5规范中的“相关的浏览上下文单元”。 该组由一个选项卡和任何其他使用Javascript代码打开的选项卡组成。 例如有ABC三个页面,在A中执行window.open(B),那么AB就是同源,B又打开了C,则ABC就是同源。 二 正文原文地址:http://hassansin.github.io/sh... 我最近看到一篇文章上说:“来自同一个源的所有窗口共享一个事件循环,它们也可以同步通信。” 照它这么说 -- 如果我在浏览器上打开了多个Tab(因为选项卡与现代浏览器中的窗口基本相同),Tab来自同一主机的不同页面,它们全都将呈现在单个线程中。 但是这没根本不可能吧,因为Chrome在其自己的进程中运行每个Tab。 他们无法共享相同的事件循环。 文章说法有待考证。 谷歌浏览器进程的模型使用chrome任务管理器的快速测试证明我是正确的。每个具有来自相同域的Tab确实是在单独的进程中运行。 但是当我在Chrome任务管理器进程中进行挖掘时,我注意到一些Tab是在相同进程ID下运行的。 例如:
Tabs甚至不是来自同一个域却在同一个进程里面。 所以这里发生了什么? 谷歌快速搜索后,事实证明,Chrome有一个复杂的流程模型:chromium.org/developers/design-documents/process-models。 默认情况下,Chrome使用process-per-site-instance模型,即: Chromium会为用户访问的每个站点实例创建一个渲染器进程。 这确保了来自不同站点的页面被独立渲染,并且对同一站点的单独访问也彼此隔离。 因此,一个站点的一个实例中的失败(例如渲染器崩溃)或资源占用率过高不会影响浏览器的其余部分。 **该模型基于内容的源和相互执行脚本的选项卡之间的关系。** 因此,两个选项卡可能会显示在任务管理器的同一个进程中,而当在已经打开的一个页面的选项卡中导航到跨站页面时,可能会切换选项卡的渲染器进程。 但事实上,我认为实际情况比上述内容更复杂。 不管那些了,我迫切地想测试一下这些Tab是否真的共享相同的Event Loop。 所以我写了一个长时间运行的同步任务。 你猜怎么了! 这只是一个空循环: function longrunning(){ for (let i=0; i<10000000000; i++); } 然后,我需要将其注入到这些tabs-per-process的其中一个中去。 有一个很好的扩展称为 窗口之间同步通信回到我刚才讨论的第一篇文章。 它也提到这些窗口还可以同步进行相互通信。 所以这些Tab必须以某种方式相互连接。 从关于Chrome进程模型的文章: 我们将一组用相互用script连接的Tabs称为一个浏览实例,它对应于HTML5规范中的“相关的浏览上下文单元”。 该组由一个选项卡和任何其他使用Javascript代码打开的选项卡组成。 这些选项卡必须在同一个进程中呈现,以允许在它们之间进行Javascript调用(最常见的是来自同一源的页面之间)。 好吧,这意味着我们需要使用JavaScript打开它们,才能连接窗口。 实际上有几种方法可以在javascript中执行此操作。 使用 演示在这里demo。 你需要让浏览器允许弹出窗口才能看到效果。 top.html: <html> <head> <title>Top window</title> <script> function longrunning(){ for(let i=0;i<2000000000;i++); } let t0 let t1 const elapsedTime = () => { if(!t0) { t0 = performance.now() t1 = t0 } else { t1 = performance.now() } return ((t1-t0)/1000).toFixed(2) } window.parentLogger = (str) => { console.log("[%s] TOP: %s",elapsedTime(),str) } window.childLogger = (str) => { console.log("[%s] CHILD: %s",str) } parentLogger('before opening popup') const popup = window.open('child.html'); // var popup = window.open('/child.html','','noopener=true'); if(popup) { parentLogger(`after popup opened,popup window url: ${popup.location.href}`) } parentLogger('starting long synchronous process. This will prevent loading and parsing of popup window') longrunning(); parentLogger('finished long synchronous process.') parentLogger('adding 1s timeout.') setTimeout(function(){ parentLogger('timed out') },1000) </script> </head> <body></body> </html> child.html: <html> <head> <title>Child window</title> <script> function longrunning(){ for(let i=0;i<2000000000;i++); } window.addEventListener('DOMContentLoaded',e => window.opener.childLogger(`popup initial html loaded,popup window url: ${window.location.href}`)) window.opener.childLogger('starting long synchronous process inside popup window. This will prevent the event loop in top window') longrunning() window.opener.childLogger('finished long synchronous process inside popup window.') // window.close() </script> </head> <body></body> </html> 不过,这里有top.html中控制台的输出: [0.00] TOP: before opening popup [0.01] TOP: after popup opened,popup window url: about:blank [0.01] TOP: starting long synchronous process. This will prevent loading and parsing of popup window [4.93] TOP: finished long synchronous process. [4.93] TOP: adding 1s timeout. [5.82] CHILD: starting long synchronous process inside popup window. This will prevent the event loop in top window [10.79] CHILD: finished long synchronous process inside popup window. [11.15] CHILD: popup initial html loaded,popup window url: http://localhost:4000/assets/chrome-process-models/child.html [11.18] TOP: timed out 你可以在每个事件的方括号中查看以秒计的总时间。
所以从弹出窗口中加载内容的时间点以及在顶部窗口中触发setTimeout回调的时间点可以清楚地看到,它们都共享相同的事件循环。 那么我们如何让同源窗口在它自己的进程中运行而不影响彼此的事件循环呢? 事实证明,我们可以在 所有这些行为在不同的浏览器中可能会有所不同。 这实际上都是特定于浏览器的实现。 我们甚至可以在Chrome中传递不同的标志并选择不同的过程模型。 三 后记这篇文章给出了最终答案:来自同一个源的Tabs共享相同的事件循环。 补充一:同一进程打开chrome的任务管理,可以看到任务情况。 window.open('https://segmentfault.com/a/1190000014833359'); 打开了一个新的Tab,但是:
在同一个进程里。 window.open('https://www.baidu.com'); 又打开了一个新的Tab,
嗯,还是同一个进程。 补充二:如何优化多个Tabs共享相同的事件循环肯定会相互影响,除了使用在 补充三:跨域通信文章讲到,它们可以同步通信。当然,很少使用window.open的方式来相互通信,但是ifame却是很常用的 -- ifram中可以加载别的域的页面。在创建了iframe之后是可以拿到ifame的实例的。然后就可以使用postMessage相互通信了。 postMessage解决了: a.) 页面和其打开的新窗口的数据传递 b.) 多窗口之间消息传递 c.) 页面与嵌套的iframe消息传递 d.) 上面三个场景的跨域数据传递 下面是一个实际的例子: <iframe id="iframe" src="http://www.domain2.com/b.html" style="display:none;"></iframe> <script> var iframe = document.getElementById('iframe'); iframe.onload = function() { var data = { name: 'aym' }; // 向domain2传送跨域数据 iframe.contentWindow.postMessage(JSON.stringify(data),'http://www.domain2.com'); }; // 接受domain2返回数据 window.addEventListener('message',function(e) { alert('data from domain2 ---> ' + e.data); },false); </script> 更加全面的跨域知识请看:前端常见跨域解决方案(全) 三 附录Process-per-site-instanceBy default,Chromium creates a renderer process for each instance of a site the user visits. This ensures that pages from different sites are rendered independently,and that separate visits to the same site are also isolated from each other. Thus,failures (e.g.,renderer crashes) or heavy resource usage in one instance of a site will not affect the rest of the browser. This model is based on both the origin of the content and relationships between tabs that might script each other. As a result,two tabs may display pages that are rendered in the same process,while navigating to a cross-site page in a given tab may switch the tab's rendering process. (Note that there are important caveats in Chromium's current implementation,discussed in the Caveats section below.) Concretely,we define a "site" as a registered domain name (e.g.,google.com or bbc.co.uk) plus a scheme (e.g.,https://). This is similar to the origin defined by the Same Origin Policy,but it groups subdomains (e.g.,mail.google.com and docs.google.com) and ports (e.g.,http://foo.com:8080) into the same site. This is necessary to allow pages that are in different subdomains or ports of a site to access each other via Javascript,which is permitted by the Same Origin Policy if they set their document.domain variables to be identical. A"site instance" is a collection of connected pages from the same site. We consider two pages as connected if they can obtain references to each other in script code (e.g.,if one page opened the other in a new window using Javascript). (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- windows – 在屏幕上保留RScript生成的图,直到用户交互
- 如何往虚拟机内传文件的3种方法
- windows-mobile – Windows Mobile设备有什么好的编程环境/
- iis-7 – 为什么要从IIS6迁移到IIS7?
- 将Windows 7安装从硬盘驱动器移动到另一个硬盘驱动器的最佳
- DACL原理.控制文件的访问权限(文件,注册表.目录.等任何带有
- windows – 为通过USB连接的某些硬件编写游戏控制器驱动程序
- Windows-8 – WinRT是否直接访问NT内核(需要操作系统支持时
- Windows下的2017 最新版 MyEclipse启动出现:a java runtim
- Windows – 如何在另一个安装程序中创建安装程序?
- windows-server-2008 – Windows 2008:WinSXS目
- IIS密钥:计算机密钥,WAS密钥,IIS配置密钥
- windows-runtime – WinRT Xaml Toolkit列系列错
- Create AD Users by Powershell
- windows – 神秘的git行为
- DOS下用XCOPY命令复制文件
- Windows.Web.Http.HttpClient.GetStringAsync 总
- windows-server-2008 – 域控制器离线几天
- 在Windows上使用TortoiseSVN更改文件名大小写
- 如何实现标准的Windows Phone 7 ComboBox替换UI