JavaScript Alert 函数执行顺序问题
code[class*="language-"],
pre[class*="language-"] {
background-color: #fdfdfd;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
margin-bottom: 1em;
}
:not(pre) > code[class*="language-"] {
position: relative;
padding: .2em;
-webkit-border-radius: 0.3em;
-moz-border-radius: 0.3em;
-ms-border-radius: 0.3em;
-o-border-radius: 0.3em;
border-radius: 0.3em;
color: #c92c2c;
border: 1px solid rgba(0, 0, 0, 0.1);
display: inline;
white-space: normal;
}
pre[class*="language-"]:before,
pre[class*="language-"]:after {
content: '';
z-index: -2;
display: block;
position: absolute;
bottom: 0.75em;
left: 0.18em;
width: 40%;
height: 20%;
max-height: 13em;
-webkit-box-shadow: 0px 13px 8px #979797;
-moz-box-shadow: 0px 13px 8px #979797;
box-shadow: 0px 13px 8px #979797;
-webkit-transform: rotate(-2deg);
-moz-transform: rotate(-2deg);
-ms-transform: rotate(-2deg);
-o-transform: rotate(-2deg);
transform: rotate(-2deg);
}
:not(pre) > code[class*="language-"]:after,
pre[class*="language-"]:after {
right: 0.75em;
left: auto;
-webkit-transform: rotate(2deg);
-moz-transform: rotate(2deg);
-ms-transform: rotate(2deg);
-o-transform: rotate(2deg);
transform: rotate(2deg);
}
.token.comment,
.token.block-comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: #7D8B99;
}
.token.punctuation {
color: #5F6364;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.function-name,
.token.constant,
.token.symbol,
.token.deleted {
color: #c92c2c;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.function,
.token.builtin,
.token.inserted {
color: #2f9c0a;
}
.token.operator,
.token.entity,
.token.url,
.token.variable {
color: #a67f59;
background: rgba(255, 255, 255, 0.5);
}
.token.atrule,
.token.attr-value,
.token.keyword,
.token.class-name {
color: #1990b8;
}
.token.regex,
.token.important {
color: #e90;
}
.language-css .token.string,
.style .token.string {
color: #a67f59;
background: rgba(255, 255, 255, 0.5);
}
.token.important {
font-weight: normal;
}
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
.namespace {
opacity: .7;
}
@media screen and (max-width: 767px) {
pre[class*="language-"]:before,
pre[class*="language-"]:after {
bottom: 14px;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
}
}
.token.tab:not(:empty):before,
.token.cr:before,
.token.lf:before {
color: #e0d7d1;
}
pre[class*="language-"].line-numbers {
padding-left: 0;
}
pre[class*="language-"].line-numbers code {
padding-left: 3.8em;
}
pre[class*="language-"].line-numbers .line-numbers-rows {
left: 0;
}
pre[class*="language-"][data-line] {
padding-top: 0;
padding-bottom: 0;
padding-left: 0;
}
pre[data-line] code {
position: relative;
padding-left: 4em;
}
pre .line-highlight {
margin-top: 0;
}
pre.line-numbers {
position: relative;
padding-left: 3.8em;
counter-reset: linenumber;
}
pre.line-numbers > code {
position: relative;
}
.line-numbers .line-numbers-rows {
position: absolute;
pointer-events: none;
top: 0;
font-size: 100%;
left: -3.8em;
width: 3em; /* works for line-numbers below 1000 lines */
letter-spacing: -1px;
border-right: 1px solid #999;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.line-numbers-rows > span {
pointer-events: none;
display: block;
counter-increment: linenumber;
}
.line-numbers-rows > span:before {
content: counter(linenumber);
color: #999;
display: block;
padding-right: 0.8em;
text-align: right;
}
-->
问题
前几天使用 JavaScript 写 HTML 页面时遇到了一个奇怪的问题:
我想实现的功能是通过 confirm()
弹窗让用户选择不同的需求,每次选择后都将选择结果暂时输出到页面上,最后一次选择结束后再一次性将选项传到后端处理。 代码类似于:
var step1 = confirm("exec step1?");
$('#result').html($('#result').html() + "\n" + step1);
var step2 = confirm("exec step2?");
$('#result').html($('#result').html() + "\n" + step2);
var step3 = confirm("exec step3?");
$('#result').html($('#result').html() + "\n" + step3);
send(step1, step2, step3);
可是代码运行后却发现:每次在执行完 confirm 函数,用户选择选项之后,页面并没有刷新,step1, step2 的结果没有实时刷新到页面上,而是到最后一步跟 step3 一块显示了出来。
后续尝试了 alert()
和 prompt()
这两个跟 confirm 类似的弹对话框函数,情况都与此相同,它们都会跳过页面渲染先被执行。
此时,还有更诡异的情况,我们给某一个 div 里赋值后,立刻 alert 此 div 里的内容,会发现 alert 显示正确的内容,而 div 里的内容却没有更新,并且会一直阻塞到我们点击确定。
如图:
alert、prompt、confirm 三个函数都类似,接下来我们就用最简单的 alert 来说。
文章欢迎转载,请尊重作者劳动成果,带上原文链接:http://www.cnblogs.com/zhenbianshu/p/8686681.html
分析
解决这个问题之前先了解一下它是怎么导致的,而要了解它需要从 JavaScript 的线程模型说起。
JavaScript 引擎是单线程运行的,浏览器无论在什么时候都只且只有一个线程在运行 JavaScript 程序,初衷是为了减少 DOM 等共享资源的冲突。可是单线程永远会面临着一个问题,那就是某一段代码阻塞会导致后续所有的任务都延迟。又由于 JavaScript 经常需要操作页面 DOM 和发送 HTTP 请求,这些 I/O 操作耗时一般都比较长,一旦阻塞,就会给用户非常差的使用体验。
于是便有了事件循环(event loop)的产生,JavaScript 将一些异步操作或 有I/O 阻塞的操作全都放到一个事件队列,先顺序执行同步 CPU代码,等到 JavaScript 引擎没有同步代码,CPU 空闲下来再读取事件队列的异步事件来依次
执行。
这些事件包括:
setTimeout()
设置的异步延迟事件;- DOM 操作相关如布局和绘制事件;
- 网络 I/O 如 AJAX 请求事件;
- 用户操作事件,如鼠标点击、键盘敲击。
解决
明白了原理, 再解决这个问题就有了方向,我们来分析这个问题:
- 由于页面渲染是 DOM 操作,会被 JavaScript 引擎放入事件队列;
alert()
是 window 的内置函数,被认为是同步 CPU代码;- JavaScript 引擎会优先执行同步代码,alert 弹窗先出现;
- alert 有特殊的阻塞性质,JavaScript 引擎的执行被阻塞住;
- 点击 alert 的“确定”,JavaScript 没有了阻塞,执行完同步代码后,又读取事件队列里的 DOM 操作,页面渲染完成。
由上述原因,导致了诡异的 “Alert执行顺序问题”。 我们无法将页面渲染变成同步操作,那么只好把 alert()
变为异步代码,从而才能在页面渲染之后执行。
对于这个解决方向,我们有两种方法可以使用:
替换 Alert() 函数
首先我们考虑替换掉 alert 函数的功能。其实大多数情况下我们替换掉 alert 并不是它不符合我们期待的执行顺序,而是因为它实在是太丑了,而且也不支持各种美化,可以想像在一个某一特定主题的网站上忽然弹出来一个灰色单调的对话框是多么不和谐。
这个我们可以考虑 Bootstrap 的 modal 模块,Bootstrap 在绝大多数网站上都在应用,而多引入一个 modal 模块也不会有多大影响。我们使用 modal 构造一个弹出对话框的样子,使用 modal 的 modal('toggle')/modal('show')/modal('hide')
方法可以很方便地控制 modal 的显隐。
替换掉对话框后,我们还需要解决后续代码执行的问题。使用 alert 函数时,我们点击确定后代码还会继续执行,而使用我们自定义的对话框可没有这种功能了,需要考虑把后续代码绑定在对话框的点击按钮上,这就需要使用 DOM 的 onclick
属性了,我们将后续函数内容抽出一个新的函数,在弹出对话框后将这个函数绑定在按钮的 onclick 事件上即可。
这里还需要注意,新函数内应该包括关闭 modal 对话框的内容。
当然,我们还可以再优化一下,抽象出来一个用来弹出对话框的函数替代 alert 函数,示例如下:
window.alert = function (message, callbackFunc) {
$('#alertContent').html(message);
$('#modal').show();
$('#confirmButton').onclick(function () {
$('#modal').hide();
callbackFunc();
});
};
如此,我们在需要弹出框时调用新的 alert 函数,并传入 callbackFunc ,在里面做后续的事情就可以了。
setTimeOut函数
当然,并不是所有人都愿意使用新的对话框替换 alert 函数的对话框,总感觉上面的方法不是特别的优雅,对此,我们可以采用另外的方法解决这个问题。
前端的同学应该对 setTimeout()
这个函数不陌生,使用它,可以延迟执行某些代码。而对于延迟执行的代码,JavaScript 引擎总是把这些代码放到事件队列里去,再去检查是否已经到了执行时间,再适时执行。代码进入事件队列,就意味着代码变成和页面渲染事件一样异步了。由于事件队列是有序
的,我们如果用 setTimeout 延时执行,就可以实现在页面渲染之后执行 alert 的功能了。
setTimeout 的函数原型为 setTimeout(code, msec)
,code 是要变为异步的代码或函数,msec 是要延时的时间,单位为毫秒。这里我们不需要它延时,只需要它变为异步就行了,所以可以将 msec 设置为 0;
同样,alert 之后的代码我们也需要处理,将它们跟 alert 一块放到 setTimeout 里异步执行。这样,代码就变为 setTimeout("alert('msg');doSomething();", 0);
,如果觉得代码不够美观或字符串不好处理的话,可以将后续代码封装成一个函数放到 doSomething()
里即可。
小结
在上面的两个解决方案中,都利用了 JavaScript 的回调函数,前者将函数所为 alert 的参数并绑定到 DOM 的 onclick 事件,后者使用 setTimeout 将函数转为异步执行。JavaScript 的回调函数确实非常强大,使用起来也很简单,但是却有一个隐含的问题,就是回调嵌套问题,单层的回调很容易理解,但如果要实现像我的需求一样,有多个 alert 和页面渲染轮流执行的情况,需要面临的可能就是“回调地狱”, onclick 事件绑定里的函数又要嵌套绑定 onclick 函数, setTimeout 里还需要另一个 setTimeout 语句,一旦出现问题,排查起来就比较麻烦了。
前端写得不多,可能对 JavaScript 的理解会有些偏差,文章如有错漏,还请在文章下面评论区指出。对于此问题,如果有大神有更好的解决方案,还请不吝赐教。
关于本文有什么问题可以在下面留言交流,如果您觉得本文对您有帮助,可以点击下面的 推荐
支持一下我,博客一直在更新,欢迎 关注
。
JavaScript Alert 函数执行顺序问题的更多相关文章
- JavaScript程序的执行顺序
JavaScript程序的执行顺序:同步==>异步==>回调 同步是阻塞模式,异步是非阻塞模式. 同步就是指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个 ...
- [Vue]vue中各选项及钩子函数执行顺序
在vue中,实例选项和钩子函数和{{}}表达式都是不需要手动调用就可以直接执行的. 一.生命周期图示 二.vue中各选项及钩子函数执行顺序 1.在页面首次加载执行顺序有如下: beforeCreate ...
- JavaScript alert()函数的使用方法
这里向大家简单介绍一下JavaScript alert()函数的使用,alert--弹出消息对话框,并且alert消息对话框通常用于一些对用户的提示信息. JavaScript alert()函数 a ...
- 通过实验窥探javascript的解析执行顺序
简介 javascript是一种解释型语言,它的执行是自上而下的.但是各浏览器对于[自上而下]的理解是有细微差别的,而代码的上下游也就是程序流对于程序正确运行又是至关重要的.所以我们有必要深入理解js ...
- JavaScript代码内部执行顺序
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- JS中函数执行顺序的问题?
作者:知乎用户链接:https://www.zhihu.com/question/23564807/answer/82996422来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注 ...
- AngularJS指令嵌套时link函数执行顺序的问题
今天研究指令嵌套时,发现子指令的link函数先于父指令的link函数执行. 这样和预想的顺序不一样. 也就是说,如果子指令的某个scope变量依赖于父指令传来的参数时,可能一直是undefinded比 ...
- Drupal7的theme函数执行顺序
theme('name') 执行顺序: 1.当前主题_name(),这个函数一般在主题的template.php文件中 2.所在模块_name() 3.theme_name() 4.name.tpl. ...
- Javascript加载执行顺序
本文主要内容 一.不同位置的script标签执行顺序 二.document.ready和window.onload的区别 一.不同位置的script标签执行顺序 整个加载的过程从解析头部开始,比如ht ...
随机推荐
- 零基础6个月学好java月薪1w+看看他是怎么学好java的
21世纪进入信息时代,信息科技给人类的生产和生活方式带来了深刻的变革,信息产业已成为推动国家经济发展的主导产业之一,Java作为含金量极高的一门IT技术,很多人希望从事这个行业,那么想学好Java,要 ...
- Fu+ 后台管理 (Thinkphp)
简要:小主从事PHP二年,期间一直做后台;为此向大家分享我制作一个后台(权限),希望能够跟各位PHP大神学习探索,如果有不对或者好的建议告知下:*~*! 1. 介绍 Fu+ 后台管理,是本人基于H+好 ...
- iOS开发--XMPPFramework--用户登录(三)
创了一个XMPP即时通讯交流群140147825,欢迎大家来交流~我们是一起写代码的弟兄~ 我们在第一篇文章中,已经介绍了Openfire服务器的搭建等环境的配置, 第二篇文章中,导入了XMPPFra ...
- iptables 学习
本博客是学习慕课网课程 用iptables搭建一套强大的安全防护盾 整理而成 iptables相当于在ip层挂载一个hook point对用户进行控制 组成: 四张表+ 五条链(hook point) ...
- Handsontable添加超链接
本文在上文的基础上,返回的数据中多了一个link超链接跳转的字段,,需要在Handsontable中显示超链接. <!DOCTYPE html> <html> <head ...
- 自签名证书和私有CA签名的证书的区别 创建自签名证书 创建私有CA 证书类型 证书扩展名【转】
自签名的证书无法被吊销,CA签名的证书可以被吊销 能不能吊销证书的区别在于,如果你的私钥被黑客获取,如果证书不能被吊销,则黑客可以伪装成你与用户进行通信 如果你的规划需要创建多个证书,那么使用私有 ...
- ubuntu字符界面怎么设置中文显示和中文输入
在ubuntu的字符登陆界面,中文显示默认是乱码的,而且也不支持中文输入,解决方法有很多, 1)安装zhcon--解决中文显示乱码的问题. sudo apt-get install zhcon 然后c ...
- 纯代码实现wordpress文章隐藏内容评论可见
在很多网站上都看过这个效果,比如说知己知彼网站,他的部分资源是需要我们评论后才能下载的,那么这个到底有什么用呢,对我而言,除了拿来装逼,还可以增加我的评论数量,不多说,先看看效果: 其实WordPre ...
- [ETL] Flume 理论与demo(Taildir Source & Hdfs Sink)
一.Flume简介 1. Flume概述 Flume是Cloudera提供的一个高可用的,高可靠的,分布式的海量日志采集.聚合和传输的系统,Flume支持在日志系统中定制各类数据发送方,用于收集数据: ...
- 支持Touch ID!EOS 项目进展速报
Daniel Larimer 周六在 Steemit 上向大家介绍了 EOS 项目的最新进展,惊喜不少. 原文链接 : https://steemit.com/eosio/@dan/eos-io-de ...