拖了几百年的三部曲终于迎来了第三篇,时过境迁,Jscex 已经更名为 wind.js 了,这里先给一下之前的链接
在之前介绍的方法中,无论是消息驱动还是 Promise,都无法摆脱「回调」这个东西。
习惯了命令式编程的我们似乎很难接受回调,因为它的执行顺序和代码编写顺序并不一致。 而 JavaScript 中的回调之所以会有这么多话题值得讨论,我想一方面是因为它有一张长得像 Java 的脸,而同时却又有如此多的异步特性。 反观像 Erlang, F# 那样生来就是异步的语言,似乎反而没这么多话题好讨论的。
异步就要回调吗?这是个问题,习惯了 JavaScript 中的异步似乎这个问题的答案是肯定的,但 Wind.js 却扭转了这一点。
在同步、阻塞的环境下,我们写下如下代码
str = readFile('...');
str += 'ok';
writeFile('...');
似乎理所当然地就认为 1、3 两行耗时操作会阻塞掉程序,于是在开发 GUI 程序的时候,遇到 IO 操作我们通常会开启新的线程来进行 IO, 然后完成时再通知主线程,这样可以避免GUI失去响应。
在 JavaScript 的世界里,用户代码只有一个线程,JS 使用异步来解决这个矛盾,与此同时使用回调的方式来达到「通知主线程」的效果。
似乎由于在此之前异步为人重视程度不是很高,JS 的异步特性被大家广为接受之后,似乎回调成为了标准的异步解决方案。 的确,回调是一种看起来很像声明的编程方法,在单一异步操作的时候,回调还算优雅,但一旦涉及异步流程控制的时候,回调嵌套就会成为挥之不去的噩梦。
回过头来看过去的同步阻塞编程方法,似乎顺序执行更讨好一些,因为代码怎么写的,程序顺序就是怎么样。
老赵开发的 Wind.js 为我们提供了「顺序编写、异步执行」的机会。 Wind.js 的原名叫 Jscex,全称 JavaScript Computation Expression,即 JavaScript 计算表达式。
计算表达式这个词是从函数式编程中来的,想象我们中学的时候解数学和物理题,我们总会用各种代数标识来表达变量,推导、化简完公式之后, 才把题目中给的具体数值代入。 这样做不仅让推导过程更加清晰可懂,还能避免中间的运算产生精度的损失。
在命令式编程中,我们写下
c = a + b;
的时候,a+b这个表达式就已经被执行计算,并且把结果赋值给 c
了。 但在函数式语言中则不尽然,由于「延迟计算」的特性,上面的代码并不一定会立即执行,而只有在它「需要被执行」(例如输出)的时候才会真正执行。
有了「计算表达式」的概念之后,我们就可以把异步操作理解为异步任务,在 Wind 里一个「异步方法」执行的时候将返回一个「异步任务」”,也称为「Task对象」。 (下文中部分 Demo 来自于 http://windjs.org/)
例如这里我们定义一个异步方法 printAsync
var printAsync = eval(Wind.compile('async', function(n){
console.log(n);
}));
当执行它的时候,并不会直接执行方法,而是会返回一个 Task
对象,可以通过
var task = printAsync(1);
task.start(); // 1
来真正启动一个 Task
到这里似乎并没有看出来异步方法和 Task
有什么用,那么我们再看看从异步方法里调用别的异步任务的情况
var fibo = eval(Wind.compile('async', function(){
var a = 1, b = 1;
console.log(a);
$await(Wind.Async.sleep(1000));
console.log(b);
while (true){
$await(Wind.Async.sleep(1000));
var c = a + b;
console.log(c);
a = b;
b = c;
}
}));
上文的代码中,在异步方法里使用了 $await
来「等待」一个异步任务,这里的异步任务是由 Wind.Async.sleep
提供的等待1秒钟的任务。
通过这样的代码实现了每隔 1 秒钟自动打印下一个斐波那契数,想一想如果不使用 Wind.js 的话似乎意味着我们需要 setInterval
了,而在 js 里写一个
while (true){
似乎也算得上是一件匪夷所思的事情了。
Wind.js 通过对异步方法中的 JS 代码进行二次编译,将「顺序编写」的代码变成了 JS 的「异步+回调」的风格, 这让我们在开发的时候可以沿用一些过去常用的思路,例如上面的例子中的 sleep
(有多少朋友初接触 JavaScript 的时候有上网搜索如何进行 sleep
的经历?)。
而 Wind.js 的其他辅助方法里也有诸如 whenAll(tasks)
这样的方法能够帮助我们进行一些多重依赖的流程控制。
上文中使用了一个短得不能再短的 Demo 来演示了 Wind.js 是如何让我们的代码可以「顺序编写、异步执行」的。 网上也有朋友怀疑这样是否违背了 JS 异步模型的初衷,我个人认为没有这个问题,编程语言为我们提供的只是一种编程语言的发明者认为比较不错的方法, 根据个人习惯和项目需要,只要语言有为我们提供了改造它的能力,我们完全可以改造它,只不过正好 JavaScript 为我们提供了改造的空间而已。 我们完全没有必要被语言特性本身绑架,束缚了编程思维,只选择以单一的模型去迎合语言特性。
在这里总结一下这个小系列中所介绍的三种梳理 JavaScript 中异步操作的方法,并谈一谈我自己的选择。
-
消息驱动——编程模型最简单、代码风格最回调。适合有「剧本」的代码,例如用它来编写一段动画的 StoryBoard 或者设计阶段就定义好的若干步骤。
-
Promises/A——API 简单易用、代码风格也不难接受。适合「剧情」不大固定,需要经常以编程方式修改异步流程的程序。
Wind.js——像是障眼法一样的可以让我们回归「传统」编程风格的工具。 适合复杂逻辑、通过异步流控制时代码显得冗长的情况,通过 $await
异步任务的方式让代码更加易读,编写也更容易。
JavaScript 中的流程控制解决方案岂止是上述三种,这里有一个巨大的列表,仔细研究的话还会找到诸如 Flow-JS、seq、Step 等等一大堆独具匠心的工具。
JavaScript 是一门非常开放的语言,由于本身的简洁和动态特性给我们带来很大「二次创作」的空间,即可以是仅仅从 API 上提供方案, 也可以通过二次编译大刀阔斧的进行改造。这正是这门语言吸引人的地方之一。
至此这个系列终于结束了,但 JavaScript 异步梳理的路才刚刚踏上起点(此时吉姆作 45° 仰望星空状)。
相关阅读:
相关推荐
在本案例中,"HTML——变幻粒子.zip" 提供了一个使用HTML实现的粒子特效。这种特效通常用于网站背景或者交互式设计,能给用户带来动态且引人入胜的视觉体验。 首先,我们来了解一下HTML在其中的作用。HTML文档是...
本资源"安卓Android源码——调用JavaScript.zip"显然是一个关于如何在Android应用中调用JavaScript的示例代码集。下面我们将深入探讨这一主题。 一、WebView组件 在Android中,`WebView`是实现调用JavaScript的核心...
JavaScript代码生成器——Coffee Script CoffeeScript是一种基于Ruby语言的编程语言,旨在通过简洁的编码方式生成JavaScript代码。它结合了Ruby的简洁和JavaScript的灵活性,使开发者可以通过简洁易读的语法撰写...
传统上,我们加载JS文件都是使用[removed]标签:[removed][removed]。...wind.js就是为了解决脚本阻塞加载而生,使用异步、并行的方式来加载外部的javascript文件(通过gzip压缩后,wind.js文件为2.6KB)。
3. **JavaScript**:JavaScript是实现网页动态功能的关键,它能让网页与用户进行交互。在这个项目中,JavaScript可能会被用来控制时间序列、响应用户点击、实现动画效果,甚至包括播放音频或视频。例如,当用户点击...
本资源“安卓Android源码——WebViewJS.zip”提供了一个关于如何在Android应用中集成WebView并与JavaScript交互的示例源码。 1. **WebView基本使用** - `WebView`是Android SDK中的一个类,它能够加载和显示网页...
JavaScript异步回调是编程中的一个核心概念,尤其是在Web开发中,因为JavaScript是单线程的,这意味着它不能同时执行多个任务。为了解决这个问题,异步编程被引入,以允许在执行其他操作的同时处理耗时的任务,如...
标题和描述中的“我知道你的宠物在想什么——双子座.zip”似乎暗示着这是一个与宠物相关的项目,可能涉及动物行为分析或宠物情绪识别的技术。标签标明了“JavaScript”,这意味着项目的核心编程语言是JavaScript,...
VC 知识库1-52期——part7.rar VC 知识库 VCKbase 1-52期 PART7
【描述】描述中的"旅游类网页设计模板——印象西藏.zip"进一步强调了该资源包是专门为设计旅游主题网站而准备的,特别是以“印象西藏”为主题,意味着它将包含与西藏文化、景点、旅行信息等相关的设计元素和页面结构...
综上所述,《JavaScript异步编程》这本书详细介绍了JavaScript中异步编程的各种技术和最佳实践,包括但不限于回调函数、事件循环、Promise、async/await等核心概念和技术。通过学习本书,读者可以更好地理解和掌握...
【标题】:“利用Google翻译实现网站国际化——js插件” 在网页开发中,为了使网站内容能够被全球用户理解和访问,网站国际化是一个重要的步骤。这个主题涉及到如何利用Google翻译API来构建一个支持多语言的网站。...
5. **JavaScript文件**:这些文件(如scripts.js)用于实现页面的客户端交互,如表单验证、AJAX异步请求等。 6. **配置文件**:可能有一个config.asp或类似的文件,包含论坛的设置,如数据库连接字符串、管理员信息...
【小程序精选源码——论坛系列】是一份专为学习者准备的资源,包含了构建论坛类小程序所需的源代码。这份压缩包旨在帮助开发者更好地理解和实践小程序的开发,特别是针对论坛类应用的设计与实现。通过深入研究这份...
在这个“网页模板——Vue.js圆形CSS3颜色渐变色拾取器”项目中,我们可以深入探讨Vue.js如何与CSS3技术结合,创建一个动态的颜色选择工具。 首先,Vue.js的核心在于其响应式数据绑定系统。通过使用`v-model`指令,...
本文将深入探讨jQuery的核心原理,以及如何在实际项目中有效地引用和使用jQuery.js文件。 一、jQuery简介 jQuery是由John Resig开发的开源JavaScript库,其主要目标是简化JavaScript的DOM操作,同时提供丰富的事件...
以太坊的JavaScript API —— web3.js介绍
kettle JavaScript代码 特殊函数
使用jessibuca.js时,开发者可以将关键的计算任务交由decoder.js或decoder.wasm来处理,从而避免JavaScript的性能限制。同时,由于WebAssembly与JavaScript的良好交互性,开发者依然可以保持代码的灵活性和易用性。 ...
4. **集成到你的工程中**:只需将编译后的库(Lib文件)链接到你的项目,同时在Debug模式下使用相应的库文件,就可以在你的应用程序中使用JavaScript功能。确保正确设置项目的依赖项和路径。 5. **调试支持**:由于...