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构建的。

相关阅读:看这一篇就够了!浅谈ES6的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(){...}(没有空格)也一样,这只是风格偏好问题。

上述代码的运行过程:

  1. it = foo()运算并没有执行生成器*foo(),而只是构造了一个迭代器(iterator),这个迭代器会控制它的执行。
  2. 第一个it.next()启动了生成器*foo(),并运行了*foo()第一行的x++
  3. *foo()yield语句处暂停,在这一点上第一个it.next()调用结束。
  4. 我们查看x的值,此时为2。
  5. 我们调用bar(),它通过x++再次递增x。
  6. 我们再次查看x的值,此时为3.
  7. 最后的it.next()调用从暂停处恢复了生成器*foo()的执行,并运行console.log(...)语句,这条语句使用当前x的值3。

相关阅读:知乎上关于生成器的解释

4.2 生成器+Promise

ES6中最完美的世界就是生成器(看似同步的异步代码)和Promise(可信任可组合)的组合。

获得Promise和生成器最大效用的最自然的方法就是yield出来一个Promise,然后通过这个Promise来控制生成器的迭代器。

推荐阅读:ECMAScript 6 入门

参考资料:《你不知道的JavaScript》(中卷) 第二部分 异步和性能

JS的异步的更多相关文章

  1. 【译】深入理解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% ...

  2. js的异步加载你真的懂吗

    面试高频之js的异步加载 讲这个问题之前, 我们从另一个面试高频问题来切入, 我们的web页面从开始解析到页面渲染完成都经历了什么 ?  1  ,  创建document对象, 开始解析页面,    ...

  3. js的异步和单线程

    最近,同事之间做技术分享的时候提到了一个问题"js的异步是另开一个线程吗?"当时为此争论不休.会后自己查阅了一些资料,对这个问题进行一个自我的分析与总结,有不同意见的希望可以赐教, ...

  4. 探秘JS的异步单线程

    对于通常的developer(特别是那些具备并行计算/多线程背景知识的developer)来讲,js的异步处理着实称得上诡异.而这个诡异从结果上讲,是由js的“单线程”这个特性所导致的. 我曾尝试用“ ...

  5. JS实现异步提交

    什么是XMLHttpRequest? XMLHttpRequest对象用于在后台与服务器交换数据 XMLHttpRequst的作用 在不重新加载页面的情况下更新网页 在页面已加载后从服务器请求数据 在 ...

  6. JS的异步世界

    前言 JS的异步由来已久,各种异步概念也早早堆在开发者面前.可现实代码中,仍然充斥了各种因异步顺序处理不当的bug,或因不好好思考,或因不了解真相.今天,就特来再次好好探索一番JS的异步世界. 01 ...

  7. JS的异步模式

    JS的异步模式:1.回调函数:2.事件监听:3.观察者模式:4.promise对象 JavaScript语言将任务的执行模式可以分成两种:同步(Synchronous)和异步(Asychronous) ...

  8. Node.js之异步编程

    > 文章原创于公众号:程序猿周先森.本平台不定时更新,喜欢我的文章,欢迎关注我的微信公众号. ![file](https://img2018.cnblogs.com/blog/830272/20 ...

  9. js 同步 异步 宏任务 微任务 文章分享

    分享一篇 写的很好的 宏任务 微任务  同步异步的文章 文章原地址: https://juejin.im/post/59e85eebf265da430d571f89 这一次,彻底弄懂 JavaScri ...

  10. co.js - 让异步代码同步化

    近期在全力开发个人网站,并且又沉淀了一些前后端的技术.近期会频繁更新. 这篇文章首发于我的个人网站:听说 - https://tasaid.com/,建议在我的个人网站阅读,拥有更好的阅读体验. 这篇 ...

随机推荐

  1. swoole中退出、异常与错误的处理笔记

    关于PHP这方面的知识 可以看 https://www.cnblogs.com/zyf-zhaoyafei/p/6928149.html 进行补课 然后下面记录一下使用swoole的时候需要注意的地方 ...

  2. mkswap命令详解

    基础命令学习目录首页 原文链接:http://blog.51cto.com/arlen99/1743841 mkswap命令用于在一个文件或者设备上建立交换分区.在建立完之后要使用sawpon命令开始 ...

  3. 深入 JSX

    从本质上讲,JSX 只是为 React.createElement(component, props, ...children) 函数提供的语法糖.JSX代码: 1 2 3 <MyButton ...

  4. 点击小图查看大图jQuery插件FancyBox魔幻灯箱

    今日发现一个不错的JQuery插件FancyBox,也许早就有这个插件了,但是没名字,我就暂且叫他魔幻灯箱吧,采用Mac系统的样式.网传主要有以下功能:■弹出的窗口有很漂亮的阴影效果.■关联的对象(就 ...

  5. Daily Scrum 11.19 部分测试报告

    下面是我们的部分测试报告: 功能测试部分: 1Exception in thread "Thread-11" java.lang.IllegalArgumentException: ...

  6. Linux基础入门--06

    简单的文本处理 实验介绍 这一节我们将介绍这几个命令:tr.col.join.paste 1.tr: -d:删除和set1匹配的字符,不是全词匹配也不是按字符顺序匹配 -s:除去指定的连续并重复的字符 ...

  7. 【每日scrum】第一次冲刺day1

    冲刺第一天,明确了自己的任务,数据分析与数据字典.

  8. 再学HTML之一

    Html 超文本标记语言 什么是html? HTML 是用来描述网页的一种语言. HTML 指的是超文本标记语言 (Hyper Text Markup Language) HTML 不是一种编程语言, ...

  9. github基础操作

    1.最简单实用的操作 更新远程仓库 git status git add . git commit -m "add" git push #git push -u origin ma ...

  10. 13_Java面向对象_第13天(static、final、匿名对象、内部类、包、修饰符、代码块)_讲义

    今日内容介绍 1.final 关键字 2.static 关键字 3.匿名对象 4.内部类 5.包的声明与访问 6.访问修饰符 7.代码块 01final关键字概念 A: 概述 继承的出现提高了代码的复 ...