JS的异步
1.异步
程序中现在运行的部分和将来运行的部分之间的关系是异步编程的核心。
多数JavaScript开发者从来没有认真思考过自己程序中的异步到底是如何出现的,以及为什么会出现,也没有探索过处理异步的其他方法。一直以来,低调的回调函数就算足够好的方法了。目前为止,还有很多人坚持认为回调函数完全够用。
但是,作为在浏览器、服务器以及其他能够想到的任何设备上运行的一流编程语言,JavaScript面临的需求日益扩大。为了满足这些需求,JavaScript的规模和复杂性也在持续增长,对异步的管理也越来越令人痛苦,这一切都迫切需要更强大、更合理的异步方法。
1.1 分块的程序
现在我们发出一个异步Ajax请求,然后在将来才能得到返回的结果(通过使用回调函数)。
//ajax(...)是某个库中提供的某个Ajax函数。
ajax("http://some.url.1",function myCallbackFunction(data){
console.log(data);//得到一些数据
});
function now(){
return 21;
}
function later(){
answer = answer * 2;
console.log("Meaning of life: ", answer);
}
var answer = now();
setTimeout(later, 1000);//Meaning of life: 42
setTimeout(...)
设置了一个事件(定时)在将来执行,所以函数later()
的内容会在之后的某个时间(从现在起1000毫秒之后)执行。
任何时候,只要把一段代码包装成一个函数,并指定它在响应某个事件(定时器、鼠标点击、Ajax响应等)时执行,你就是在代码中创建了一个将来执行的块,也由此在这个程序中引入了异步机制。
1.2 事件循环
现在我们来澄清一件事情(可能令人震惊):尽管你显然能够编写异步JavaScript代码,但直到最近(ES6),JavaScript才真正内建有直接的异步概念。
JavaScript引擎并不是独立运行的,它运行在宿主环境中,对多数开发者来说通常就是Web浏览器。经过最近几年的发展,JavaScript已经超过了浏览器的范围,进入了其他环境,比如通过像Node.js这样的工具进入服务器领域。实际上,JavaScript现如今已经嵌入到了从机器人到电灯泡等各种各样的设备中。
所有这些环境都提供了一种机制来处理程序中多个块的执行,且执行每个块时调用JavaScript引擎,这种机制被称为事件循环。
ES6中Promise对事件循环队列的调度运行能够直接进行精细控制。
1.3 并行
异步是关于现在和将来的时间间隙,而并行是关于能够同时发生的事情。
var a = 20;
function foo(){
a = a + 1;
}
function bar(){
a = a * 2;
}
ajax("...",foo);
ajax("...",bar);
由于JavaScript的单线程特性,foo()
和bar()
中的代码具有原子性。也就是说,一旦foo()
开始运行,它的所有代码都会在bar()
中的任意代码运行之前完成,或者相反。这称为完整运行特性。
1.4 并发
两个或多个“进程”同时执行就出现了并发。这里的“进程”之所以打上引号,是因为这并不是计算机科学意义上的真正操作系统级进程。这是虚拟进程,或者任务,表示一个逻辑上相关的运算序列。
1.5 任务
在ES6中,有一个新的概念建立在事件循环队列之上,叫作任务队列。这个概念给大家带来的最大影响可能是Promise的异步特性。
事件循环队列类似于一个游乐园游戏:玩过了一个游戏之后,你需要重新到队尾排队才能再玩一次。而任务队列类似于玩过了游戏之后,插队接着继续玩。
2.回调
回调是编写和处理JavaScript程序异步逻辑的最常用方式。
回调函数是JavaScript的异步主力军,并且它们不辱使命地完成了自己的任务。
2.1 continuation
//A
ajax("...",function(data){
//C
});
//B
//A
和//B
表示程序的前半部分,而//C
标识了程序的后半部分。前半部分立刻执行,然后是一段时间不确定的停顿。在未来的某个时刻,如果Ajax调用完成,程序就会从停下的位置继续执行后半部分。
信任的问题
//C
会延迟到将来发生,并且在第三方的控制下。我们把这称为控制反转,也就是把自己程序一部分的执行控制交给某个第三方。在你的代码和第三方工具之间有一份并没有明确表达的契约。
//过分信任输入
function addNumbers(x,y){
return x + y;
}
addNumbers(21,21);//42
addNumbers(21,"21");//"2121"
//针对不信任输入的防御性代码
function addNumbers(x,y){
if(typeof x != "number" || y != "number"){
throw Error("Bad parameters");
}
return x + y;
}
addNumbers(21,21);//42
addNumbers(21,"21");//Error: "Bad parameters"
//依旧安全但更好一些
function addNumbers(x,y){
x = Number(x);
y = Number(y);
return x + y;
}
addNumbers(21,21);//42
addNumbers(21,"21");//42
3.Promise
通过回调表达程序异步和管理并发的两个主要缺陷:缺乏顺序性和可信任性。
我们用回调函数来封装程序中的continuation,然后把回调交给第三方,期待其能够调用回调,实现正确的功能。通过这种形式,我们要表达的意思是:“这是将来要做的事情,要在当前的步骤完成之后发生”。
如果我们不把自己程序的continuation传给第三方,而是希望第三方给我们提供了解其任务何时结束的能力,然后我们自己的代码来决定下一步做什么。这种范式就称为Promise。
绝大多数JavaScript/DOM平台新增的异步API都是基于Promise构建的。
4.生成器
我们把注意力转移到一种顺序、看似同步的异步流程控制表达风格。使这种风格成为可能的“魔法”就是ES6生成器(generator)。
4.1 打破完整运行
var x = 1;
//下面是生成器函数
function *foo(){
x++;
yield;//暂停点
console.log("x: ",x);
}
function bar(){
x++;
}
var it = foo();//构造迭代器
it.next();//启动foo()
x;//2
bar();
x;//3
it.next();//x: 3
注意:function* foo(){...}
、function *foo(){...}
是一样的,唯一区别是*
位置的风格不同。function*foo(){...}
(没有空格)也一样,这只是风格偏好问题。
上述代码的运行过程:
it = foo()
运算并没有执行生成器*foo()
,而只是构造了一个迭代器(iterator),这个迭代器会控制它的执行。- 第一个
it.next()
启动了生成器*foo()
,并运行了*foo()
第一行的x++
。 *foo()
在yield
语句处暂停,在这一点上第一个it.next()
调用结束。- 我们查看x的值,此时为2。
- 我们调用
bar()
,它通过x++
再次递增x。 - 我们再次查看x的值,此时为3.
- 最后的
it.next()
调用从暂停处恢复了生成器*foo()
的执行,并运行console.log(...)
语句,这条语句使用当前x的值3。
相关阅读:知乎上关于生成器的解释
4.2 生成器+Promise
ES6中最完美的世界就是生成器(看似同步的异步代码)和Promise(可信任可组合)的组合。
获得Promise和生成器最大效用的最自然的方法就是yield出来一个Promise,然后通过这个Promise来控制生成器的迭代器。
推荐阅读:ECMAScript 6 入门
参考资料:《你不知道的JavaScript》(中卷) 第二部分 异步和性能
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的异步由来已久,各种异步概念也早早堆在开发者面前.可现实代码中,仍然充斥了各种因异步顺序处理不当的bug,或因不好好思考,或因不了解真相.今天,就特来再次好好探索一番JS的异步世界. 01 ...
- JS的异步模式
JS的异步模式:1.回调函数:2.事件监听:3.观察者模式:4.promise对象 JavaScript语言将任务的执行模式可以分成两种:同步(Synchronous)和异步(Asychronous) ...
- Node.js之异步编程
> 文章原创于公众号:程序猿周先森.本平台不定时更新,喜欢我的文章,欢迎关注我的微信公众号. ![file](https://img2018.cnblogs.com/blog/830272/20 ...
- js 同步 异步 宏任务 微任务 文章分享
分享一篇 写的很好的 宏任务 微任务 同步异步的文章 文章原地址: https://juejin.im/post/59e85eebf265da430d571f89 这一次,彻底弄懂 JavaScri ...
- co.js - 让异步代码同步化
近期在全力开发个人网站,并且又沉淀了一些前后端的技术.近期会频繁更新. 这篇文章首发于我的个人网站:听说 - https://tasaid.com/,建议在我的个人网站阅读,拥有更好的阅读体验. 这篇 ...
随机推荐
- OLAP和OLTP的区别
OLAP(On-Line Analytical Processing)联机分析处理,也称为面向交易的处理过程,其基本特征是前台接收的用户数据可以立即传送到计算中心进行处理,并在很短的时间内给出处理结果 ...
- MyCat安装与测试教程 超详细!
MyCat安装与测试教程 超详细! MyCat基础知识 一.什么是MYCAT? 1. 一个彻底开源的,面向企业应用开发的大数据库集群 2. 支持事务.ACID.可以替代MySQL的加强版数据库 3. ...
- Tree - Information Theory
This will be a series of post about Tree model and relevant ensemble method, including but not limit ...
- Plasma Cash 合约解读
作者介绍 虫洞社区·签约作者 steven bai Plasma Cash 合约解读 Plasma Cash 合约解读 1. 合约代码 2. 合约文件简单介绍 3. Plasma Cash 的基础数据 ...
- Docker部署Golang
1. 安装docker 2. mkdir myDocker 3. cd myDocker && touch Dockerfile 4. Dockerfile写入 # 将golang ...
- Apache 性能配置优化
前言 最近在进行apache性能优化设置.在修改apache配置)文件之前需要备份原有的配置文件夹conf,这是网站架设的好习惯.以下的apache配置调优均是在red had的环境下进行的. htt ...
- 20162314 《Program Design & Data Structures》Learning Summary Of The Ninth Week
20162314 2017-2018-1 <Program Design & Data Structures>Learning Summary Of The Ninth Week ...
- 20162328蔡文琛 week05 大二
20162328 2017-2018-1 <程序设计与数据结构>第5周学习总结 教材学习内容总结 集合是收集元素并组织其他对象的对象. 集合中的元素一般由加入集合的次序或元素之间的某些固有 ...
- 每日Scrum(10)
今天我们小组整合了下我们所编辑的程序,然后在界面上进行了修改和少部分的完善,现在就等着下午的验收了 任务展板 燃尽图如下:
- P2P通讯原理
1.简介 当今互联网到处存在着一些中间件(MIddleBoxes),如NAT和防火墙,导致两个(不在同一内网)中的客户端无法直接通信.这些问题即便是到了IPV6时代也会存在,因为即使不需要NAT,但还 ...