JavaScript异步精讲,让你更加明白Js的执行流程!
问题点
- 什么是单线程,和异步有什么关系
- 什么是 event-loop
- jQuery的Deferred
- Promise 的基本使用和原理
- async/await(和 Promise的区别、联系)
一、什么是单线程,和异步有什么关系
- 单线程- 只有一个线程,只能做一件事
- 原因-避免DOM渲染的冲突
- 解决方案-异步
1) 单线程- 只有一个线程,只能做一件事
基础事例
// 循环运行期间,JS执行和DOM 渲染暂时卡顿 var i, sum = 0; for (i = 0; i<100000000000; i++) { sum += i; } console.log(sum); // alert不处理,JS执行和DOM 渲染暂时卡顿 console.log(1); alert('hello'); console.log(2)
2) 原因 - 避免DOM渲染的冲突
- 浏览器需要渲染DOM
- JS可以修改DOM结构
- 所以JS执行的时候,浏览器 DOM 渲染会暂停
- 两段JS也不能同时执行(都修改 DOM 就冲突了)
- webworker支持多线程,但是不能修改访问 DOM
3) 解决方案 - 异步
基础事例
console.log(100) setTimeout(function () { console.log(200); // 反正1000ms之后执行 }, 1000); // 先不管他,先让其它 JS 代码运行 console.log(300); console.log(400);
4) 异步 - 存在的问题
问题一:没有按照书写方式执行,可读性差
问题二:callback中不容易模块化
二、什么event-loop
文字解释
- 事件轮询,JS实现异步的具体解决方案
- 同步代码,直接执行
- 异步函数先放在异步队列中
- 待同步函数执行完毕,轮询执行异步队列函数
例子分析
例子一:
先看左下角,是我们要执行的代码,第一个是延迟100毫秒打印1,第二个是延迟0,打印2,按照上面讲的,同步代码先执行,异步代码放在异步队列中,最后执行完同步在执行异步队列的函数,执行第一个函数的时候,setTimeout是个异步函数,那我们是不是应该立即放在右侧的队列中呢?答案是否定的,因为它有一个延时,我们需要100毫秒之后才能放进去,执行第二个setTimeout函数,由于没有延时,所以会被放进异步队列中,最后一个是直接 在主进程,所以 直接执行打印,等同步执行完之后,立刻看异步队列中,这时候只有第二个setTimeout,执行完之后,在去看异步队列有没有,因为100这毫秒对于计算机来说,是一个相当长的时间,所以js引擎会一直轮询异步队列中有没有可执行函数,直接100毫秒之后第一个setTimeout被放进异队列中,然后才执行。
例子一:
上图流程跟例子一一样,这边我们疑惑的是,当我们执行 ajax 的时候,里面的success 是异步函数,它要什么时候被放进异步队列中呢?
当然是请求成功的时候被放进异步队列中,但我们不知道是 大于100毫秒还是小于,所以打印结果有两种情况,先打印d c b a,或者打印 d c a b。
三、是否用过jQuery的Deferred
jQuery 1.5的变化 -1.5之前
jQuery 1.5的变化 -1.5之后
jQuery 1.5的变化
无法改变JS异步和单线程的本质
只能从写法上杜绝 callback 这种形式
它是一种语法糖形式,但是解藕了代码
很好的体现:开放封装原则
1) 什么是deferred对象?
开发网站的过程中,我们经常遇到某些耗时很长的javascript操作。其中,既有异步的操作(比如ajax读取服务器数据),也有同步的操作(比如遍历一个大型数组),它们都不是立即能得到结果的。
通常的做法是,为它们指定回调函数(callback)。即事先规定,一旦它们运行结束,应该调用哪些函数。
但是,在回调函数方面,jQuery的功能非常弱。为了改变这一点,jQuery开发团队就设计了deferred对象。
简单说,deferred对象就是jQuery的回调函数解决方案。
2) deferred应用
我们来看一个具体的例子。假定有一个很耗时的操作wait:
var wait = function() { var tasks = function() { alert('执行完毕'); }; setTimeout(tasks,5000); }
我们为它指定回调函数,应该怎么做呢?
你可能会使用$when()
$.when(wait()) .done(function(){ alert('成功!') }) .fail(function() { alert('出错!'); })
但是,这样写的话,done()方法会立即执行,起不到回调函数的作用。原因在于$.when()的参数只能是deferred对象,所以必须对wait()进行改写:
var dtd = $.Deferred(); //新建一个deferred var wait = function (dtd) { var tasks = function () { console.log('执行完毕'); dtd.resolve(); //改变deferred对象执行状态 }; setTimeout(tasks, 5000); return dtd; }
现在,wait()函数返回的是deferred对象,这就可以加上链式操作了。
$.when(wait(dtd))
.done(function(){ alert("成功!"); })
.fail(function(){ alert("出错!"); });
更多内容可参考这里
四、Promise 的基本使用和原理
1) 什么是 Promise
一人Promise 对象代表一个目前还不可用,但是在未来的某个时间点可以被解析的值。它允许你以一种同步的方式编写异步代码。Promise的出现,原本是为了解决回调地狱的问题。所有人在讲解Promise时,都会以一个ajax请求为例,此处我们也用一个简单的ajax的例子来带大家看一下Promise是如何使用的。
ajax请求的传统写法:
getData(method, url, successFun, failFun){ var xmlHttp = new XMLHttpRequest(); xmlHttp.open(method, url); xmlHttp.send(); xmlHttp.onload = function () { if (this.status == 200 ) { successFun(this.response); } else { failFun(this.statusText); } }; xmlHttp.onerror = function () { failFun(this.statusText); }; }
改为promise后的写法:
getData(method, url){ var promise = new Promise(function(resolve, reject){ var xmlHttp = new XMLHttpRequest(); xmlHttp.open(method, url); xmlHttp.send(); xmlHttp.onload = function () { if (this.status == 200 ) { resolve(this.response); } else { reject(this.statusText); } }; xmlHttp.onerror = function () { reject(this.statusText); }; }) return promise; } getData('get','www.xxx.com').then(successFun, failFun)
很显然,我们把异步中使用回调函数的场景改为了.then()、.catch()等函数链式调用的方式。基于promise我们可以把复杂的异步回调处理方式进行模块化。
2)Promise的原理分析
其实 promise 原理说起来并不难,它内部有三个状态,分别是 pending, fulfilled 和 rejected。
peding 是对象创建后的初始状态,当对象fulfill(成功)时变为 fulfilled, 当对象 reject(失败)时变为 rejected。且只能从 pending 变为 fulfilled 或 rejected,而不能逆向或或从 fulfilled 变为rejected,从 rejected变为 fulfilled。如图所示:
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
java修饰符使用指南
java修饰符使用指南 1、什么是修饰符? 指的是一种标识类型以及类型成员的访问范围的声明。 应用在修饰类名,类成员,方法,参数,构造器中。 2、修饰符的有几种? 一共大致有14种,分别为public、private、protected、static、final、 synchronized、volatile、transient、native、interface、abstract、 strictfp、enum、annotation。 对于这些,我们有些可能很熟悉,有些可能很陌生。总之,一半一半吧。我们先从源码分析。 3、java源码 package java.lang.reflect; import java.security.AccessController; import sun.reflect.LangReflectAccess; import sun.reflect.ReflectionFactory; /** * The Modifier class provides {@code static} methods and * constants to decode class ...
- 下一篇
Nginx常见配置
Nginx常见配置 一、全局层 #有1个工作的子进程,可以自行修改,但太大无益,因为要争夺CPU,一般设置为 CPU数*核数 worker_processes 1; #一般是配置nginx连接的特性,如1个word能同时允许多少连接 Event { #这是指一个子进程最大允许连1024个连接 worker_connections 1024; } #这是配置http服务器的主要段 http { #这是虚拟主机段 Server1 { #定位,把特殊的路径或文件再次定位,如image目录单独处理,如.php单独处理 Location { ... } } Server2 { ... } } 二、HTTP服务器层 1. 网页内容的压缩编码与传输速度优化 请求: Accept-Encoding:gzip,deflate,sdch 响应: Content-Encoding:gzip Content-Length:36093 原理: 浏览器---请求----> 声明可以接受 gzip压缩 或 deflate压缩 或compress 或 sdch压缩。 从http协议的角度看--请求头 声明 ac...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Mario游戏-低调大师作品
- CentOS6,CentOS7官方镜像安装Oracle11G
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Windows10,CentOS7,CentOS8安装Nodejs环境
- Docker安装Oracle12C,快速搭建Oracle学习环境
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- CentOS8编译安装MySQL8.0.19
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS8安装Docker,最新的服务器搭配容器使用
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7