js中的同步和异步

  自从读了研后,走上了学术之路,天天除了看论文就是作实验,最后发现本身仍是喜欢开发呀,因而我又重回前端啦~javascript

  隔了这么久没学前端,好像不少东西都忘了很多,并且不得不说前端的技术更新是真的快,接下来将会从新拾起前端的一点一滴,首先进入的是js的同步和异步的世界~前端

 

1、单线程java

(1)单线程的概念  ajax

  若是你们熟悉java,应该都知道,java是一门多线程语言,咱们经常能够利用java的多线程处理各类各样的事,好比说文件上传,下载等,而JavaScript是否也能够支持多线程呢?chrome

  答案是否认的,JavaScript是一门单线程的语言,所以,JavaScript在同一个时间只能作一件事,单线程意味着,若是在同个时间有多个任务的话,这些任务就须要进行排队,前一个任务执行完,才会执行下一个任务,好比说下面这段代码编程

 

// 同步代码
function fun1() {
  console.log(1);
}
function fun2() {
  console.log(2);
}
fun1();
fun2();

// 输出
1
2

 

  很容易能够看出,输出会依次输入1,2,由于代码是从上到下依次执行,执行完fun1(),才继续执行fun2(),可是若是fun1()中的代码执行的是读取文件或者ajax操做,文件的读取和数据的获取都须要必定时间,难道咱们须要彻底等到fun1()执行完才能继续执行fun2()么?为了解决这个问题,后面咱们会介绍同步和异步的概念promise

(2)为何是单线程浏览器

    其实,JavaScript的单线程,与它的用途是有很大关系,咱们都知道,JavaScript做为浏览器的脚本语言,主要用来实现与用户的交互,利用JavaScript,咱们能够实现对DOM的各类各样的操做,若是JavaScript是多线程的话,一个线程在一个DOM节点中增长内容,另外一个线程要删除这个DOM节点,那么这个DOM节点到底是要增长内容仍是删除呢?这会带来很复杂的同步问题,所以,JavaScript是单线程的服务器

 

2、同步任务和异步任务数据结构

(1)为何会有同步和异步

    由于JavaScript的单线程,所以同个时间只能处理同个任务,全部任务都须要排队,前一个任务执行完,才能继续执行下一个任务,可是,若是前一个任务的执行时间很长,好比文件的读取操做或ajax操做,后一个任务就不得不等着,拿ajax来讲,当用户向后台获取大量的数据时,不得不等到全部数据都获取完毕才能进行下一步操做,用户只能在那里干等着,严重影响用户体验

    所以,JavaScript在设计的时候,就已经考虑到这个问题,主线程能够彻底不用等待文件的读取完毕或ajax的加载成功,能够先挂起处于等待中的任务,先运行排在后面的任务,等到文件的读取或ajax有告终果后,再回过头执行挂起的任务,所以,任务就能够分为同步任务和异步任务

(2)同步任务

    同步任务是指在主线程上排队执行的任务,只有前一个任务执行完毕,才能继续执行下一个任务,当咱们打开网站时,网站的渲染过程,好比元素的渲染,其实就是一个同步任务

(3)异步任务

    异步任务是指不进入主线程,而进入任务队列的任务,只有任务队列通知主线程,某个异步任务能够执行了,该任务才会进入主线程,当咱们打开网站时,像图片的加载,音乐的加载,其实就是一个异步任务

function fun1() {
  console.log(1);
}
function fun2() {
  console.log(2);
}
function fun3() {
  console.log(3);
}
fun1();
setTimeout(function(){
  fun2();
},0);
fun3();

// 输出
1
3
2

    有了异步,就算fun2()里面是文件的读取或ajax这种须要耗时的任务,也不怕fun3()要等到fun2()执行完才能执行啦

(4)异步机制

    那么,JavaScript中的异步是怎么实现的呢?那要须要说下回调和事件循环这两个概念啦

    首先要先说下任务队列,咱们在前面也介绍了,异步任务是不会进入主线程,而是会先进入任务队列,任务队列实际上是一个先进先出的数据结构,也是一个事件队列,好比说文件读取操做,由于这是一个异步任务,所以该任务会被添加到任务队列中,等到IO完成后,就会在任务队列中添加一个事件,表示异步任务完成啦,能够进入执行栈啦~可是这时候呀,主线程不必定有空,当主线程处理完其它任务有空时,就会读取任务队列,读取里面有哪些事件,排在前面的事件会被优先进行处理,若是该任务指定了回调函数,那么主线程在处理该事件时,就会执行回调函数中的代码,也就是执行异步任务啦

    单线程从从任务队列中读取任务是不断循环的,每次栈被清空后,都会在任务队列中读取新的任务,若是没有任务,就会等到,直到有新的任务,这就叫作任务循环,由于每一个任务都是由一个事件触发的,所以也叫做事件循环

    总的来讲,JavaScript的异步机制包括如下几个步骤

 

(1)全部同步任务都在主线程上执行,行成一个执行栈
(2)主线程以外,还存在一个任务队列,只要异步任务有告终果,就会在任务队列中放置一个事件
(3)一旦执行栈中的全部同步任务执行完毕,系统就会读取任务队列,看看里面还有哪些事件,那些对应的异步任务,因而结束等待状态,进入执行栈,开始执行
(4)主线程不断的重复上面的第三步

 

 

3、异步编程

    那么,怎么才能实现异步编程,写出性能更好的代码呢,下面有几种方式

(1)回调函数

    回调函数是实现异步编程最简单的方法啦,回调函数咱们在使用ajax时应该用的不少啦,其实在使用ajax时,咱们就用到了异步

 

var req = new XMLHttpRequest();
req.open("GET",url);
req.send(null);
req.onreadystatechange=function(){}

 

    req.send()方法是 AJAX  向服务器发生数据,它是一个异步任务,而 req.onreadystatechange()属于事件回调,借由浏览器的HTTP请求线程发起对服务器的请求,在请求获得响应以后触发请求完成事件,将回调函数推入事件队列等待执行

    其实像setTimeout,还有咱们平时为元素绑定监听事件,和上面说的道理也是同样的

    回调函数的优势是简单、容易理解和部署,缺点是不利于代码的阅读和维护,各个部分之间高度耦合(Coupling),流程会很混乱,并且每一个任务只能指定一个回调函数

(2)Promise

    一直以来,JavaScript处理异步都是以callback的方式,在前端开发领域callback机制几乎深刻人心,近几年随着JavaScript开发模式的逐渐成熟,CommonJS规范顺势而生,其中就包括提出了Promise规范,Promise彻底改变了js异步编程的写法,让异步编程变得十分的易于理解,同时Promise也已经归入了ES6,并且高版本的chrome、firefox浏览器都已经原生实现了Promise,只不过和现现在流行的类Promise类库相比少些API

    Promise包括如下几个规范

  • 一个promise可能有三种状态:等待(pending)、已完成(fulfilled)、已拒绝(rejected)
  • 一个promise的状态只可能从“等待”转到“完成”态或者“拒绝”态,不能逆向转换,同时“完成”态和“拒绝”态不能相互转换
  • promise必须实现then方法(能够说,then就是promise的核心),并且then必须返回一个promise,同一个promise的then能够调用屡次,而且回调的执行顺序跟它们被定义时的顺序一致
  • then方法接受两个参数,第一个参数是成功时的回调,在promise由“等待”态转换到“完成”态时调用,另外一个是失败时的回调,在promise由“等待”态转换到“拒绝”态时调用,同时,then能够接受另外一个promise传入,也接受一个“类then”的对象或方法,即thenable对象

    在使用Promise时,咱们须要检测一些浏览器是否支持Promise

 

if(typeof(Promise)==="function") {
    console.log("支持");
}
else {
    console.log("不支持");
}

 

    咱们可使用new Promise进行Promise的建立

function wait(time) {
    return new Promise(function(resolve,reject) {
        setTimeout(resolve,time);
    });
}

    这个时候咱们就可使用Promise处理异步任务啦

wait(1000).then(function(){
    console.log(1);
})

    上面这个例子表示1秒后输出1,一样的道理,咱们可使用Promise进行更加复杂的操做,关于更多的操做,就不继续说啦,关于异步的实现,其实还有其它的一些方法,可是由于上面说的这两种方法用的比较多,因此就只说上面这两种了

相关文章
相关标签/搜索