JS异步任务和异步加载

刚开始我区分不了异步任务和异步加载,感觉两者是很像的东西。
随着时间发现两者是形容不同的事物。
比如异步任务,讲的是event loop的执行规则,是怎么让function异步执行。
而异步加载则讲的更多的是在渲染页面时,如何让浏览器在下载JS的同时,还会进行后续页面的处理。

一、JS异步任务

JS是单线程语言,如果都按照顺序进行,往往会出现浏览器无响应的情况,所以就需要异步的形式。

1.JS中任务的形式

JS中所有的任务可以分为两种:同步任务和异步任务。

  • 同步任务:在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。
    这种模式的好处是实现起来比较简单,执行环境相对单纯。
    坏处是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。常见的浏览器无响应(假死),往往就是因为某一段Javascript代码长时间运行(比如死循环),导致整个页面卡在这个地方,其他任务无法执行。

  • 异步任务:不进入主线程,而进入任务队列中的任务,只有任务队列通知主线程,某个异步任务可以执行了,这个任务才会进入主线程执行。

2.异步的解决方案

1. Promise
function ajax(url){
    return new Promise(function(resolve,reject){
        var xhr=new XMLHttpRequest();
        xhr.onload=function(){
            resolve(this.responseText);
        };
        xhr.onerror=reject;
        xhr.open('GET',url);
        xhr.send();
    });
}
ajax('/echo/json')
    .then(function(result){...})
    .then(function(){...})
    .catch(function(){...});

Promise代表了一个异步操作,可以将异步对象和回调函数脱离开来,通过.then方法在这个异步操作上绑定回调函数,Promise可以让我们通过链式调用的方法去解决回调嵌套的问题,而且由于promise.all这样的方法存在,可以让同时执行多个操作变得简单。

promise对象存在三种状态:

1)Fulfilled:成功状态
2)Rejected:失败状态
3)Pending:既不是成功也不是失败状态,可以理解为进行中状态

promise对象的两个重要方法:resolve/reject

1).resolve方法可以使Promise对象的状态改变为成功,同时传递一个参数用于后续成功后的操作。
2).reject方法可以将Promise对象的状态改变为失败,同时将错误信息传递到后续错误处理的操作。
3).then可以使用链式调用,原因在于:每一次执行该方法时总会返回一个Promise对象。
另外,在then的函数当中的返回值,可以作为后续操作的参数(例如:.then(return a).then(console.log(a+b)))

promise异常处理

如果代码异步操作抛出错误,会调用catch方法指定的回调函数,处理这个错误,而且then方法指定的回调函数,如果运行中抛出错误,也会被catch捕获。Promise对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止,也就是说,错误总是会被下一个catch语句捕获。

理解Promise用法的关键点:
  • 1.then方法是Promise实例的方法,即Promise.prototype上的,它的作用是为Promise实例添加状态改变时的回调函数,这个方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数。
  • 2.链式中的第二个then开始,它们的resolve中的参数,是前一个then中resolve的return语句的返回值。
  • 3.关于执行顺序:Promise在实例化的时候就会执行,也就是如果Promise的实例化语句中函数console.log输出语句,它会比then中的先执行。Promise.all中传入的Promise对象的数组(假设为p1、p2),即使p2的运行速度比p1快,Promise.all方法仍然会按照数组中的顺序将结果返回。
Promise的缺点:

1.当处于未完成状态时,无法确定目前处于哪一阶段。
2.如果不设置回调函数,Promise内部的错误不会反映到外部。
3.无法取消Promise,一旦新建它就会立即执行,无法中途取消。

2.async/await

很多人说async/await是异步编程的终极解决方案、
JavaScript 的 async/await 实现,离不开 Promise。

async:定义异步函数

1)自动把函数转换为Promise
2)当调用异步函数时,函数返回值会被resolve处理
3)异步函数内部可以使用await

await:暂停异步函数的执行

1)当使用在Promise前面时,await等待Promise完成,并返回Promise的结果
2)await只能和Promise一起使用,不能和callback一起使用
3)await只能用在async函数中

async/await并不会取代promise,因为async/await底层依然使用promise。
下面有两个例子:

async function getABC(){
    let A = await getValueA(); // getValueA 花费 2 秒
    let B = await getValueB(); // getValueA 花费 4 秒
    let C = await getValueC(); // getValueA 花费 3 秒
    return A*B*C
}

每次遇到 await 关键字时,Promise 都会停下在,一直到运行结束,所以总共花费是 2+4+3 = 9 秒

async function getABC() {
    // Promise.all() 允许同时执行所有的异步函数
    let results = await Promise.all([ getValueA, getValueB, getValueC ]); 
    return results.reduce((total,value) => total * value);
}

函数总耗时为 4 秒(getValueB 的耗时)。

Async 的价值在于用写同步的方式写异步,1避免了阻塞,2必免写回调

二、异步加载

JS在默认情况下是以同步模式(又称阻塞模式)加载的,浏览器对于代码请求的资源都是瀑布式的加载,而不是阻塞式的,但是JS的执行总是阻塞式的。

这会引起什么问题?如果在页面中加载一些JS,但其中某个请求迟迟得不到响应,位于此JS后的JS将无法执行,同时页面渲染也不能继续。

异步加载又被称为非阻塞加载,浏览器在下载JS的同时,还会进行后续页面处理。

1.如何实现JS异步加载

  • 动态生成<script>标签:这种方法可以兼容所有浏览器。
  • async属性
    async是HTML5的新属性,该属性规定一旦脚本可用,则会异步执行(一旦下载完毕就会立刻执行),需要注意的是,async属性仅适用于外部脚本。
<script type="text/javascript" src='http://china-addthis.googlecode.com/svn/trunk/addthis.js' async="async"></script>
  • defer属性
    defer属性规定是否对该脚本的执行进行延迟,知道页面加载为止。
 <script type="text/javascript" src='http://china-addthis.googlecode.com/svn/trunk/addthis.js' defer="defer"></script

来分析一下async和defer属性:

a.如果没有async属性和defer属性,那么浏览器会立即执行当前的JS脚本,阻塞后面的脚本;
b.如果有async属性,加载和渲染后续文档的过程和当前JS的加载和执行并行进行,它是乱序执行的,不管你声明的顺序如何,只要它加载完了就会执行。
c.如果有defer属性,加载后续文档元素的过程和JS的加载时并行进行的,但是JS的执行是在所有元素解析完成之后进行的,而且它是按照加载顺序执行脚本的。

简言之:async是乱序;defer是顺序。

-$(document).ready
需要引用jquery
兼容所有浏览器

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 一、Javascript实现异步编程的过程以及原理 1、为什么要用Javascript异步编程 众所周知,Java...
    Ebony_7c03阅读 872评论 0 2
  • 前言 我们知道Javascript语言的执行环境是"单线程"。也就是指一次只能完成一件任务。如果有多个任务,就必须...
    浪里行舟阅读 14,432评论 1 27
  • 由于JavaScript是单线程的语言,因此异步编程对于js的重要程度可想而知,可以说没有异步的js程序寸步难行。...
    会飞小超人阅读 1,008评论 0 7
  • jjcancan阅读 216评论 0 0
  • 先生们,女士们,各位观众朋友们,又到了一周一次的牙齿治疗日了,让我们来看看,今天会发生什么事情呢,下面为患者自述…...
    Depressed猪阅读 580评论 0 51