一、什么是异步?

我们一般喜欢把异步和同步、并行拿出来比较,我以前的理解总是很模糊,总是生硬地记着“同步就是排队执行,异步就是一起执行”,现在一看,当初简直就是傻,所以我们第一步先把这三个概念搞清楚,我不太喜欢看网上有些博客里很含糊地说“xxxx是同步,xxxx是异步”,还有举什么通俗的例子,其实对不懂的人来说还是懵逼。

首先我们要知道这一切的根源都是“Javascript是单线程”,也就是一次只能做一件事,那么为什么是单线程呢?因为js渲染在浏览器上,包含了许多与用户的交互,如果是多线程,那么试想一个场景:一个线程在某个DOM上添加内容,而另一个线程删除这个DOM,那么浏览器要如何反应呢?这就乱套了。

单线程下所有的任务都是需要排队的,而这些任务分为两种:同步任务和异步任务,同步任务就是在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入任务队列(task queue)的任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。所以说同步执行其实也是一种只有主线程的异步执行。这里有一个视频关于异步操作是如何被执行的,讲得非常好《what the hack is event loop》,我给大家画个图再来理解一下。

event-loop.png

这里补充说明下不同的异步操作添加到任务队列的时机不同,如 onclick, setTimeout, ajax 处理的方式都不同,这些异步操作是由浏览器内核的 webcore 来执行的,webcore 包含上面提到的3种 webAPI,分别是 DOM Binding、timer、network模块。
onclick 由浏览器内核的 DOM Binding 模块来处理,当事件触发的时候,回调函数会立即添加到任务队列中。
setTimeout 会由浏览器内核的 timer 模块来进行延时处理,当时间到达的时候,才会将回调函数添加到任务队列中。
ajax 则会由浏览器内核的 network 模块来处理,在网络请求完成返回之后,才将回调添加到任务队列中。
最后再来说下并行,并行是关于能够同时发生的事情,是一种多线程的运行机制,而不管同步异步都是单线程的。

二、为什么要用异步操作

这个很好理解,同步下前一个事件执行完了才能执行后一个事件,那么要是遇到Ajax请求这种耗时很长的,那页面在这段时间就没法操作了,卡在那儿,更有甚者,万一这个请求由于某种原因一直没有完成,那页面就block了,很不友好。

三、如何实现异步

我们可以通过回调函数Promise生成器Async/Await等来实现异步。
今天我们先说最基础的回调函数处理方法来实现,列举几个大家熟悉的使用场景,比如:ajax请求、IO操作、定时器。

ajax(url, function(){
//这就是回调函数
});
setTimeOut(function(){
//回调函数
}, 1000)

回调本身是比较好用的,但是随着Javascript越来越成熟,对于异步编程领域的发展,回调已经不够用了,体现在以下几点:

1、大脑处理程序是顺序的,对于复杂的回调函数会不易理解,我们需要一种更同步、更顺序的方式来表达异步。
举例说明:

//回调函数实现两数相加
function add(getX, getY, cb){
var x, y;
getX(function(xVal){
x=xVal;
if(y!=undefined){
cb(x+y);
}
});
getY(function(){
y=yVal;
if(x!=undefined){
cb(x+y);
}
});
}
add(fetchX, fetchY, function(sum){
console.log(sum);
}) //Promise实现两数相加
function add(xPromise, yPromise){
return Promise.all([xPromise, yPromise])
.then(function(values){
return value[0] + value[1];
});
}
//fetchX()、fetchY()返回相应值的Promise
add(fetchX(), fetchY())
.then(function(sum){
console.log(sum);
})

只看结构是不是Promise的写法更顺序话一些。
2、回调一般会把控制权交给第三方,从而带来信任问题,比如:

  • 调用回调过早
  • 调用回调过晚(或未调用)
  • 调用回调次数过多或过少
  • 未能传递所需的环境和参数
  • 吞掉可能出现的错误和异常

而Promise的特性就有效地解决了这些问题,它是如何解决的呢?

调用回调过早

这种顾虑主要是代码是否会引入类Zalgo效应,也就是一个任务有时会同步完地成,而有时会异步地完成,这将导致竟合状态。
Promise被定义为不能受这种顾虑的影响,因为即便是立即完成的Promise(比如 new Promise(function(resolve){ resolve(42); }))也不可能被同步地 监听。也就是说,但你在Promise上调用then(..)的时候,即便这个Promise已经被解析了,你给then(..)提供的回调也将总是被异步地调用。

调用回调过晚

当一个Promise被调用时,这个Promise 上的then注册的回调函数都会在下一个异步时机点上,按顺序地,被立即调用。这些回调中的任意一个都无法影响或延误对其它回调的调用。
举例说明:

p.then( function(){
p.then( function(){
console.log( "C" );
} );
console.log( "A" );
} );
p.then( function(){
console.log( "B" );
} );
// A B C

为什么“C”没有排到“B”的前面?因为因为“C”所处的.then回调函数是在下一个事件循环tick。

回调未调用

这是一个很常见的顾虑。Promise用几种方式解决它。
首先,当Promise被解析后,在代码不出错的情况下它一定会告知你解析结果。如果代码有错误,归类于后面的“吞掉错误或异常”中。
那如果Promise本身不管怎样永远没有被解析呢?那么Promise会用Promise.race来解决。
看代码示例:

// 一个使Promise超时的工具
function timeoutPromise(delay) {
return new Promise( function(resolve,reject){
setTimeout( function(){
reject( "Timeout!" );
}, delay );
} );
} // 为`foo()`设置一个超时
Promise.race( [
foo(), // 尝试调用`foo()`
timeoutPromise( 3000 ) // 给它3秒钟
] )
.then(
function(){
// `foo(..)`及时地完成了!
},
function(err){
// `foo()`不是被拒绝了,就是它没有及时完成
// 那么可以考察`err`来知道是哪种情况
}
);
调用次数过少或过多

正常是调用一次,“过少”就是未被调用,参考上文;“过多”的情况也很容易理解。Promise的定义方式使得它只能被决议一次,如果出于某种情况决议了多次,Promise也只会接受第一次决议,并忽略后续调用。

未能传递所需的参数/环境值

Promise只会有一个解析结果(完成或拒绝)。如果没有用一个值明确地解析它,它的值就是undefined,就像JS中常见的那样。

吞掉错误或异常

Promise中异常会被捕获,并且使这个Promise被拒绝。
举个例子:

var p = new Promise( function(resolve,reject){
foo.bar(); // `foo`没有定义,所以这是一个错误!
resolve( 42 ); // 永远不会跑到这里 :(
} ); p.then(
function fulfilled(){
// 永远不会跑到这里 :(
},
function rejected(err){
// `err`将是一个来自`foo.bar()`那一行的`TypeError`异常对象
}
);

Promise就先说到这里,关于PromiseAPI及其源码还有生成器、Async/Await 在后续文章中整理报道。
【写得不好的地方请大胆吐槽,非常感谢大家带我进步。】

参考资料:
阮一峰event-loop
王福朋深入理解javascript异步系列一
你不知道的javascript
你不懂JS: 异步与性能 第三章: Promise(上)

原作者:程序媛Wendy
链接:https://www.jianshu.com/p/f4abe8c4fc2f

js异步处理的更多相关文章

  1. JS魔法堂:深究JS异步编程模型

    前言  上周5在公司作了关于JS异步编程模型的技术分享,可能是内容太干的缘故吧,最后从大家的表情看出"这条粉肠到底在说啥?"的结果:(下面是PPT的讲义,具体的PPT和示例代码在h ...

  2. 纯js异步无刷新请求(只支持IE)

    纯js异步无刷新请求 下载地址:http://pan.baidu.com/s/1slakL1F 所以因为非IE浏览器都禁止跨域请求,所以以只支持IE. <HTML> <!-- 乱码( ...

  3. js异步编程

    前言 以一个煮饭的例子开始,例如有三件事,A是买菜.B是买肉.C是洗米,最终的结果是为了煮一餐饭.为了最后一餐饭,可以三件事一起做,也可以轮流做,也可能C需要最后做(等A.B做完),这三件事是相关的, ...

  4. C“中断” 与 JS“异步回调” 横向对比

    在底层C语言中,有一个非常重要而特别的概念,叫做“中断”.用比喻来说,我正在写着博客,突然我妈打个电话过来,我就离开了键盘去接电话了,然后写博客就中断了,我聊完电话回来再继续写.乍一听似乎并没有什么大 ...

  5. 利用ajaxfileupload.js异步上传文件

    1.引入ajaxfileupload.js 2.html代码 <input type="file" id="enclosure" name="e ...

  6. 深究JS异步编程模型

    前言  上周5在公司作了关于JS异步编程模型的技术分享,可能是内容太干的缘故吧,最后从大家的表情看出"这条粉肠到底在说啥?"的结果:(下面是PPT的讲义,具体的PPT和示例代码在h ...

  7. 点评js异步加载的4种方式

    主要介绍了点评js异步加载的4种方式,帮助大家更全面的了解js异步加载方式,感兴趣的小伙伴们可以参考一下 js异步加载的4种方式,点评开始. <!DOCTYPE html> <htm ...

  8. JS异步加载的三种方式

    js加载的缺点:加载工具方法没必要阻塞文档,过得js加载会影响页面效率,一旦网速不好,那么整个网站将等待js加载而不进行后续渲染等工作. 有些工具方法需要按需加载,用到再加载,不用不加载,. 默认正常 ...

  9. 关于JS异步加载方案

    javascript延迟加载的解决方案: 1.使用defer标签 <span style="font-size: small;"><script type=&qu ...

  10. js异步加载 defer和async 比较

    网上说法很多,很少一句话能总结清楚的,终于找到两句一针见血的描述,很到位: 相同点:都不阻塞DOM解析 defer  :顺序:保证先后顺序.解析:HTML 解析器遇到它们时,不阻塞(脚本将被异步下载) ...

随机推荐

  1. 教你如何创建vue环境

    教你如何创建vue的环境 wdnmd我操作了一万年,终于成功创建了vue的环境,现在就来讲一下,到底应该怎么操作才能成功创建vue的 第一步 : 我们应该做的不是别的,而是去官方网站下载文件 node ...

  2. HDU 1387 Team Queue( 单向链表 )

    Team Queue Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total ...

  3. java虚拟机规范(se8)——class文件格式(三)

    4.5 字段 字段使用field_info结构来描述. 在同一个class文件中的两个字段不能有相同的名称和描述符. 结构的格式如下: field_info { u2 access_flags; u2 ...

  4. JavaScript学习笔记(基础部分)

    一.JavaScript简介: 概念:JavaScript是一种解释性的.跨平台的.基于对象的脚本语言,一般用于客户端来给HTML页面增加动态的功能. 组成: 1.ECMAScript,描述了该语言的 ...

  5. js常用扩展方法

    在日常的开发过程中,经常会碰到javaScript原生对象方法不够用的情况,所以经常会对javaScript原生方法进行扩展.下面就是在实际工作时,经常使用的一些方法,做一下记录,有需要的可以拿去. ...

  6. elasticsearch Java High Level REST 相关操作封装

    pox.xml文件添加以下内容 <dependency> <groupId>org.elasticsearch.client</groupId> <artif ...

  7. 转载:对比Angular/jQueryUI/Extjs:没有一个框架是万能的

    Angular不能做什么?对比Angular/jQueryUI/Extjs 框架就好比兵器,你得明白你手里拿的是屠龙刀还是倚天剑,刀法主要是砍,剑法主要是刺.对于那些职业喷子和脑残粉,小僧送你们两个字 ...

  8. 三步完成Source Insight 4.0 破解安装(转)

    转自:https://blog.csdn.net/biubiuibiu/article/details/78044232 三步完成Source Insight 4.0 破解安装   下载地址有更新,之 ...

  9. [转]DesignWare是什么

    一.DesignWare是什么 摘自https://zhidao.baidu.com/question/473669077.html DesignWare是SoC/ASIC设计者最钟爱的设计IP库和验 ...

  10. 卸载Anaconda

    conda install anaconda-clean anaconda-clean --yes Anaconda 安装的时候在 .bash_profile中添加了变量: export PATH=& ...