JS的异步世界
前言
JS的异步由来已久,各种异步概念也早早堆在开发者面前。可现实代码中,仍然充斥了各种因异步顺序处理不当的bug,或因不好好思考,或因不了解真相。今天,就特来再次好好探索一番JS的异步世界。
01 异步的由来--单线程
上世纪末,互联网仍处于极慢速时代,穿梭于客户端与服务端的请求,对于时间的耗费是如此的奢侈。而即将面世的LiveScript,便被网景公司考虑同时在浏览器和服务端使用,在浏览器端对表单进行校验,从而提高表单提交效率。为了将这一脚本语言推向市场,网景与sun联合开发,最终以Java冠名为JavaScript。
刚面世的JavaScript,是为网页设计人员准备的,不需要太复杂的语言设计,能简单上手,自然就是最好的。
于是,单线程,弱类型,一开始就成为了JavaScript的基因。而其中的单线程,便是最戏剧性的存在,Ryan Dahl因为JavaScript是单线程语言,从而选择了js开发了轻量级服务器(nodejs),使得js从浏览器端延伸到服务器。随着JS开发队伍和程序复杂度的同步发展,异步处理成为了JS程序的重中之重。
02 JS是一个充满异步的世界
先来导入几个异步的常见场景
dom用户输入响应
ducument.addEventListener('click', function(){})
Ajax
$.ajax(<url>, function() {})
定时/延时
setTimeout(function() {}, 1000)
setInterval(function() {}, 1000)
文件读取
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function() {}
以上的场景基本有个共同特性,耗时!
举个栗子,我们去银行取钱,当人很多时,如果还是排队模式,会耗费很多时间(同步模式)。于是设立了取号机,取了号,不用排队,在一旁坐着,安心打开电脑写个文档,等叫号后再去办业务(异步模式)。
同理,由于单线程的特性,当JS应用越来越复杂,耗时的程序如果以同步来进行,就会阻塞js的单线程,如大水冲过狭窄的河道,势必决堤。那JS是怎么开拓导流渠道的呢?其实在js的单线程(主线程)背后,规律的运行了很多线程:
- dom事件处理线程
- http请求线程
- 定时器线程
- ...
这些线程就充当了JS大江的小河道,当短时有大流量时,接纳吸收,将过滤处理后的正常水流,再汇入JS主干道。
与其说JS是单线程,不如说JS是有着自动化多线程处理的主线程。无需手动编码介入新开线程,切换线程,消息同步等冗繁的处理。专用线程会接管相关任务,并将处理结果送回主线程进行顺序处理。
说到这里稍微提一下web worker,虽然是自定义的多线程,最终还是子线程地位,仍旧将处理完成的结果以回调函数方式汇入到主线程进行异步处理。
03 异步处理一般流程
先看以下代码,异步模式开始了
var img = new Image()
var imgLoadCallback = function() {}
img.src = 'http://????'
img.onload = callback
“http君,麻烦帮取一个图片数据,好了后交给imgLoadCallback君。” — js主线程老大
“任务收到,您先忙,图片请求交给我了,好了之后我叫imgLoadCallback君到休息室排队,您空了通知下 Event Loop巡检官。” — http请求线程
img.src = 'http://????'
img.onload = imgLoadCallback
“图片已取到,imgLoadCallback君去休息室排队等候吧!” — http请求线程
imgLoadCallback入栈JS任务队列
“刚好忙完手上的事情了,Event Loop君,帮看下休息室有没有人排队” — JS主线程老大
“老大,已把等候者imgLoadCallback叫过来处理任务” — Event Loop巡检官
执行imgLoadCallback
“事情都交给合适的人去办了,突然就清闲下来了,老大就是要这样当啊,嘿嘿嘿… Event Loop君,定时看下休息室有没有人排队吧… ” — JS主线程老大
JS主线程通过Event Loop读取任务队列
讲完故事,再来看这张异步示意图,是否能理解了?

04 回调处理工具的进化
从前面的篇章已经能看出来了,异步处理的结果是通过回调放置到任务队列转接到主线程中的。
北京猿人刀跟火种,这么写异步回调,看上去也能令人接受。
$.ajax(
url: '自家香蕉树林',
data: {
picker: '猴子A'
},
success: function(data) {
$.ajax(
url: '隔壁老孙家桃林',
data: {
exchanges: data.香蕉,
buyer: '猴子A'
},
success: function(data) {
console.log('向本猴王进贡', data.桃子)
}
)
}
)
进化成人类后交易过程变的复杂了,于是就变成回调地狱,传说中的callback hell
$.ajax(
url: '自家香蕉树林',
data: {
picker: '老王'
},
success: function(data) {
$.ajax(
url: '集市贩卖',
data: {
goods: data.香蕉,
seller: '老王'
},
success: function(data) {
$.ajax(
url: '隔壁老李桃子铺',
data: {
exchanges: data.钱,
buyer: '老王'
},
success: function(data) {
console.log('向本王进贡', data.桃子)
}
)
}
)
}
)
于是发明了铁器promise,解决回调地狱之痛
$.ajax(
url: '自家香蕉树林',
data: {
picker: '老王'
}
)
.then(function(data) {
return $.ajax(
url: '集市贩卖',
data: {
goods: data.香蕉,
seller: '老王'
}
)
})
.then(function(data) {
return $.ajax(
url: '隔壁老李桃子铺',
data: {
exchanges: data.钱,
buyer: '老王'
}
)
})
.then(function(data) {
console.log('向本王进贡', data.桃子)
})
关于promise的升级版async、await,本篇不多说了,理念上基本一致。
继续...
这下一次命令,只会来供给本王一次桃子,每次都要发令,好麻烦,得下个令让老王每天去卖香蕉买桃子,给我月供100个,于是就发生了以下的故事
var contributeTime;
setInterval(function(){
$.ajax(
url: '自家香蕉树林',
data: {
picker: '老王'
}
)
.then(function(data) {
return $.ajax(
url: '集市贩卖',
data: {
goods: data.香蕉,
seller: '老王'
}
)
})
.then(function(data) {
return $.ajax(
url: '隔壁老李桃子铺',
data: {
exchanges: data.钱,
buyer: '老王'
}
)
})
.then(function(data) {
var currentTime = new Date().getTime();
if (!contributeTime || (currentTime - contributeTime > '月')) {
console.log('向本王进贡', [data.桃子,…]); //length=100
currentTime = contributeTime;
}
})
}, '天')
这过程,好像也太不优雅了点。
ReactX的JS版,RxJs来了,将异步看作为单点,将其扩展了时间线,作为流来处理。所以对于一次又一次的进贡,都可进行时序管理,于是整个过程变成这样:
import { ajax } from 'rxjs/ajax'; //此处特别写引入,目的为不与jquery.ajax混淆
import { interval } from 'rxjs';
const ob = interval('天');
const peachPay = ob
.pipe(switchMap(x => ajax.post('自家香蕉树林', {picker: '老王'})))
.pipe(switchMap(data => ajax.post('集市贩卖', {seller: '老王', goods: data.香蕉})))
.pipe(switchMap(data => ajax.post('隔壁老李桃子铺', {buyer: '老王', exchanges: data.钱})))
.pipe(throttle(data => interval('月')))
.subscribe(data => console.log(`每月收到月供:${data.桃子.length}个${data.桃子}`));
整个过程顺着管道不断变换处理,就是一条全自动流水线!然鹅,然鹅,并一定每月就能供出100个桃子啊,万一遇到农灾,或者经济萧条…
以上例子仅提供思路,且读且珍重!
05 比工具更重要的,是理解
前端开发中,诸多剪不断理还乱的偶现bug来源于异步处理的顺序混乱。即便是异步处理工具越来越先进,由于代码层面的顺序和真实执行顺序的不一致,也还是容易一不小心犯错误。
异步处理工具不是万能的,还是需不断将异步原理内化入思维模式中,种码的时候,就需清晰的知道该段代码会什么时候结出果实。
再注:以上代码未经运行验证,仅示意流程与思路。望各路大神多多包涵!如有思路上都提供错误的,求板砖~
本文为原创,如需转载,请注明作者及来源,多谢~
JS的异步世界的更多相关文章
- 【译】深入理解python3.4中Asyncio库与Node.js的异步IO机制
转载自http://xidui.github.io/2015/10/29/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3python3-4-Asyncio%E5%BA%93% ...
- js的异步加载你真的懂吗
面试高频之js的异步加载 讲这个问题之前, 我们从另一个面试高频问题来切入, 我们的web页面从开始解析到页面渲染完成都经历了什么 ? 1 , 创建document对象, 开始解析页面, ...
- js的异步和单线程
最近,同事之间做技术分享的时候提到了一个问题"js的异步是另开一个线程吗?"当时为此争论不休.会后自己查阅了一些资料,对这个问题进行一个自我的分析与总结,有不同意见的希望可以赐教, ...
- 探秘JS的异步单线程
对于通常的developer(特别是那些具备并行计算/多线程背景知识的developer)来讲,js的异步处理着实称得上诡异.而这个诡异从结果上讲,是由js的“单线程”这个特性所导致的. 我曾尝试用“ ...
- JS实现异步提交
什么是XMLHttpRequest? XMLHttpRequest对象用于在后台与服务器交换数据 XMLHttpRequst的作用 在不重新加载页面的情况下更新网页 在页面已加载后从服务器请求数据 在 ...
- JS的异步模式
JS的异步模式:1.回调函数:2.事件监听:3.观察者模式:4.promise对象 JavaScript语言将任务的执行模式可以分成两种:同步(Synchronous)和异步(Asychronous) ...
- Node.js之异步编程
> 文章原创于公众号:程序猿周先森.本平台不定时更新,喜欢我的文章,欢迎关注我的微信公众号. 或者修改了 Java 的选项,通常比较难判断系统是否已经按照你的修改进行了配置和启动.这个页面将会帮助你查看 Confluence 站点运行使用的系统属性. 你 ...
- python之+=与+(转载)
先看一个简单的例子 从程序分析,进行直接+操作后,python会重新生成一个对象,而进行+=操作并不改变原来的对象,是在原来对象的基础上进行操作,所以+=也称为就地加 除此之外+和+=还有不同: 从程 ...
- python 与mongodb 交互
创建管理员 1 > use admin 2 switched to db admin 3 > db 4 admin 5 > db.createUser({user:'admin',p ...
- ionic3 出现莫名广告
应用上线出现 有莫名其妙的广告弹出. 1,DNS被劫持 2,第三方包带广告 3,Http被劫持 wifi和4G网都出现了广告,所以可以直接排除DNS被劫持的问题 广告页只会在H5的页面出现,所以基本可 ...
- python并发编程之多进程1-----------互斥锁与进程间的通信
一.互斥锁 进程之间数据隔离,但是共享一套文件系统,因而可以通过文件来实现进程直接的通信,但问题是必须自己加锁处理. 注意:加锁的目的是为了保证多个进程修改同一块数据时,同一时间只能有一个修改,即串行 ...
- laravel 频率限制throttle
在 Laravel 5.6 中,还引入了频率限制功能.所谓频率限制,指的是在指定时间单个用户对某个路由的访问次数限制,该功能有两个使用场景,一个是在某些需要验证/认证的页面限制用户失败尝试次数,提高系 ...
- 【转】运维DBA的4大纪律9项注意
朋友们调侃说,运维是个把脑袋别在裤腰带上的活,更有人说,运维是个把脑袋别在他人裤腰带上的活,苦劳没人认,有锅就有得背! 测试的同学说,“吃瓜群众很难感知运维背后的付出,倒是出了事情更能体现我们的专业性 ...
- 在centos6.8上源码安装MySQL
1.安装环境:软件包:mysql-5.6.31.tar.gz 需求相关选项: 安装基目录basedir:/mydb/mysql31数据存放目录datadir:/mydb/mysql31/data端口号 ...
- extjs5(项目中文件的加载过程)
现在来看看js类加载过程.如下图所示: 1、首先:浏览器中输入 localhost:1841 ,调用 index.html; <!DOCTYPE HTML> <html> &l ...
- 目标检测算法之YOLOv1与v2
YOLO:You Only Look Once(只需看一眼) 基于深度学习方法的一个特点就是实现端到端的检测,相对于其他目标检测与识别方法(如Fast R-CNN)将目标识别任务分成目标区域预测和类别 ...