0

0

JavaScript多线程的实现方法_javascript技巧

php中文网

php中文网

发布时间:2016-05-16 19:13:42

|

1447人浏览过

|

来源于php中文网

原创

注:以下内容基于IE中GIF的onload事件的基础上,故所有测试IE only
需要用到的几个图片

先看一个简单的事实:

Matlab语言的特点 中文WORD版
Matlab语言的特点 中文WORD版

本文档主要讲述的是Matlab语言的特点;Matlab具有用法简单、灵活、程式结构性强、延展性好等优点,已经逐渐成为科技计算、视图交互系统和程序中的首选语言工具。特别是它在线性代数、数理统计、自动控制、数字信号处理、动态系统仿真等方面表现突出,已经成为科研工作人员和工程技术人员进行科学研究和生产实践的有利武器。希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看

下载
复制代码 代码如下:




如果不出所料的话你的IE应该弹出一大堆alert提示窗口了。注意是“一大堆”!

原因很简单:IE对多帧GIF的onload事件重复执行,即每播放完一次动画,就重新执行一次onload事件。

(注:按下ESC键会停止gif动画的播放,故也会停止onload事件执行)

利用这一特性我们可以模拟多线程的实现:
看下面的代码:

复制代码 代码如下:

image A onload执行次数:0

image B onload执行次数:0

image C onload执行次数:0



是不是有点意思了?
再看下面的代码:

复制代码 代码如下:





部分代码引自Rimifon。(转载 http://Dnew.cn) 
JavaScript多线程编程简介
虽然有越来越多的网站在应用AJAX技术进行开发,但是构建一个复杂的AJAX应用仍然是一个难题。造成这些困难的主要原因是什么呢?是与服务器的异步通信问题?还是GUI程序设计问题呢?通常这两项工作都是由桌面程序来完成的,那究竟为何开发一个可以实现同样功能的AJAX应用就这么困难呢?

AJAX 开发中的难题
让我们通过一个简单的例子来认识这个问题。假设你要建立一个树形结构的公告栏系统(BBS),它可以根据用户请求与服务器进行交互,动态加载每篇文章的信息,而不是一次性从服务器载入所有文章信息。每篇文章有四个相关属性:系统中可以作为唯一标识的ID、发贴人姓名、文章内容以及包含其所有子文章ID的数组信息。首先假定有一个名为getArticle()的函数可以加载一篇文章信息。该函数接收的参数是要加载文章的ID,通过它可从服务器获取文章信息。它返回的对象包含与文章相关的四条属性:id,name,content和children。例程如下:


复制代码 代码如下:

function ( id ) {
var a = getArticle(id);
document.writeln(a.name + "" + a.content);
}

然而你也许会注意到,重复用同一个文章ID调用此函数,需要与服务器之间进行反复且无益的通信。想要解决这个问题,可以考虑使用函数 getArticleWithCache(),它相当于一个带有缓存能力的getArticle()。在这个例子中,getArticle()返回的数据只是作为一个全局变量被保存下来:
复制代码 代码如下:

var cache = {};
function getArticleWithCache ( id ) {
if ( !cache[id] ) {
cache[id] = getArticle(id);
}
return cache[id];
}

现在已将读入的文章缓存起来,让我们再来考虑一下函数backgroundLoad(),它应用我们上面提到的缓存机制加载所有文章信息。其用途是,当读者在阅读某篇文章时,从后台预加载它所有子文章。因为文章数据是树状结构的,所以很容易写一个递归的算法来遍历树并且加载所有的文章:
复制代码 代码如下:

function backgroundLoad ( ids ) {
for ( var i=0; i var a = getArticleWithCache(ids[i]);
backgroundLoad(a.children);
}
}

backgroundLoad ()函数接收一个ID数组作为参数,然后通过每个ID调用前面定义过的getArticldWithCache()方法,这样就把每个ID对应的文章缓存起来。之后再通过已加载文章的子文章ID数组递归调用backgroundLoad()方法,如此整个文章树就被缓存起来。

到目前为止,一切似乎看起来都很完美。然而,只要你有过开发AJAX应用的经验,你就应该知晓这种幼稚的实现方法根本不会成功,这个例子成立的基础是默认 getArticle()用的是同步通信。可是,作为一条基本原则,JavaScript要求在与服务器进行交互时要用异步通信,因为它是单线程的。就简单性而言,把每一件事情(包括GUI事件和渲染)都放在一个线程里来处理是一个很好的程序模型,因为这样就无需再考虑线程同步这些复杂问题。另一方面,他也暴露了应用开发中的一个严重问题,单线程环境看起来对用户请求响应迅速,但是当线程忙于处理其它事情时(比如说调用getArticle()),就不能对用户的鼠标点击和键盘操作做出响应。

如果在这个单线程环境里进行同步通信会发生什么事情呢?同步通信会中断浏览器的执行直至获得通信结果。在等待通信结果的过程中,由于服务器的调用还没有完成,线程会停止响应用户并保持锁定状态直到调用返回。因为这个原因,当浏览器在等待服务器响应时它不能对用户行为作出响应,所以看起来像是冻结了。当执行 getArticleWithCache()和backgroundLoad()会有同样的问题,因为它们都是基于getArticle()函数的。由于下载所有的文章可能会耗费很可观的一段时间,因此对于backgroundLoad()函数来说,浏览器在此段时间内的冻结就是一个很严重的问题——既然浏览器都已经冻结,当用户正在阅读文章时就不可能首先去执行后台预加载数据,如果这样做连当前的文章都没办法读。

如上所述,既然同步通信在使用中会造成如此严重的问题,JavaScript就把异步通信作为一条基本原则。因此,我们可以基于异步通信改写上面的程序。 JavaScript要求以一种事件驱动的程序设计方式来写异步通信程序。在很多场合中,你都必须指定一个回调程序,一旦收到通信响应,这个函数就会被调用。例如,上面定义的getArticleWithCache()可以写成这样:
复制代码 代码如下:

var cache = {};
function getArticleWithCache ( id, callback ) {
if ( !cache[id] ) {
callback(cache[id]);
} else {
getArticle(id, function( a ){
cache[id] = a;
callback(a);
});
}
}

这个程序也在内部调用了getArticle()函数。然而需要注意的是,为异步通信设计的这版getArticle()函数要接收一个函数作为第二个参数。当调用这个getArticle()函数时,与从前一样要给服务器发送一个请求,不同的是,现在函数会迅速返回而非等待服务器的响应。这意味着,当执行权交回给调用程序时,还没有得到服务器的响应。如此一来,线程就可以去执行其它任务直至获得服务器响应,并在此时调用回调函数。一旦得到服务器响应, getArticle()的第二个参数作为预先定义的回调函数就要被调用,服务器的返回值即为其参数。同样的,getArticleWithCache ()也要做些改变,定义一个回调参数作为其第二个参数。这个回调函数将在被传给getArticle()的回调函数中调用,因而它可以在服务器通信结束后被执行。

单是上面这些改动你可能已经认为相当复杂了,但是对backgroundLoad()函数做得改动将会更复杂,它也要被改写成可以处理回调函数的形式:
复制代码 代码如下:

function backgroundLoad ( ids, callback ) {
var i = 0;
function l ( ) {
if ( i getArticleWithCache(ids[i++], function( a ){
backgroundLoad(a.children, l);
});
} else {
callback();
}
}
l();
}

改动后的backgroundLoad()函数看上去和我们以前的那个函数已经相去甚远,不过他们所实现的功能并无二致。这意味着这两个函数都接受ID数组作为参数,对于数组里的每个元素都要调用getArticleWithCache(),再应用已经获得子文章ID递归调用backgroundLoad ()。不过同样是对数组的循环访问,新函数中的就不太好辨认了,以前的程序中是用一个for循环语句完成的。为什么实现同样功能的两套函数是如此的大相径庭呢?

这个差异源于一个事实:任何函数在遇到有需要同服务器进行通信情况后,都必须立刻返回,例如getArticleWithCache()。除非原来的函数不在执行当中,否则应当接受服务器响应的回调函数都不能被调用。对于JavaScript,在循环过程中中断程序并在稍后从这个断点继续开始执行程序是不可能的,例如一个for语句。因此,本例利用递归传递回调函数实现循环结构而非一个传统循环语句。对那些熟悉连续传送风格(CPS)的人来说,这就是一个 CPS的手动实现,因为不能使用循环语法,所以即便如前面提到的遍历树那么简单的程序也得写得很复杂。与事件驱动程序设计相关的问题是控制流问题:循环和其它控制流表达式可能比较难理解。

这里还有另外一个问题:如果你把一个没有应用异步通信的函数转换为一个使用异步通信的函数,那么重写的函数将需要一个回调函数作为新增参数,这为已经存在的APIs造成了很大问题,因为内在的改变没有把影响限于内部,而是导致整体混乱的APIs以及API的其它使用者的改变。

造成这些问题目的根本原因是什么呢?没错,正是JavaScript单线程机制导致了这些问题。在单线程里执行异步通信需要事件驱动程序设计和复杂的语句。如果当程序在等待服务器的响应时,有另外一个线程可以来处理用户请求,那么上述复杂技术就不需要了。

试试多线程编程
让我来介绍一下Concurrent.Thread,它是一个允许JavaScript进行多线程编程的库,应用它可以大大缓解上文提及的在AJAX开发中与异步通信相关的困难。这是一个用JavaScript写成的免费的软件库,使用它的前提是遵守Mozilla Public License和GNU General Public License这两个协议。你可以从他们的网站 下载源代码。

马上来下载和使用源码吧!假定你已经将下载的源码保存到一个名为Concurrent.Thread.js的文件夹里,在进行任何操作之前,先运行如下程序,这是一个很简单的功能实现:
复制代码 代码如下:




执行这个程序将会顺序显示从0开始的数字,它们一个接一个出现,你可以滚屏来看它。现在让我们来仔细研究一下代码,他应用while(1)条件制造了一个不会中止的循环,通常情况下,象这样不断使用一个并且是唯一一个线程的JavaScript程序会导致浏览器看起来象冻结了一样,自然也就不会允许你滚屏。那么为什么上面的这段程序允许你这么做呢?关键之处在于while(1)上面的那条Concurrent.Thread.create()语句,这是这个库提供的一个方法,它可以创建一个新线程。被当做参数传入的函数在这个新线程里执行,让我们对程序做如下微调:
复制代码 代码如下:




在这个程序里有个新函数f()可以重复显示数字,它是在程序段起始定义的,接着以f()为参数调用了两次create()方法,传给create()方法的第二个参数将会不加修改地传给f()。执行这个程序,先会看到一些从0开始的小数,接着是一些从100,000开始的大数,然后又是接着前面小数顺序的数字。你可以观察到程序在交替显示小数和大数,这说明两个线程在同时运行。

让我来展示Concurrent.Thread的另外一个用法。上面的例子调用create()方法来创建新线程。不调用库里的任何APIs也有可能实现这个目的。例如,前面那个例子可以这样写:
复制代码 代码如下:




在script 标签内,很简单地用JavaScript写了一个无穷循环。你应该注意到标签内的type属性,那里是一个很陌生的值(text/x- script.multithreaded-js),如果这个属性被放在script标签内,那么Concurrent.Thread就会在一个新的线程内执行标签之间的程序。你应当记住一点,在本例一样,必须将Concurrent.Thread库包含进来。

有了Concurrent.Thread,就有可能自如的将执行环境在线程之间进行切换,即使你的程序很长、连续性很强。我们可以简要地讨论下如何执行这种操作。简言之,需要进行代码转换。粗略地讲,首先要把传递给create()的函数转换成一个字符串,接着改写直至它可以被分批分次执行。然后这些程序可以依照调度程序逐步执行。调度程序负责协调多线程,换句话说,它可以在适当的时候做出调整以便每一个修改后的函数都会得到同等机会运行。 Concurrent.Thread实际上并没有创建新的线程,仅仅是在原本单线程的基础上模拟了一个多线程环境。

虽然转换后的函数看起来是运行在不同的线程内,但是实际上只有一个线程在做这所有的事情。在转换后的函数内执行同步通信仍然会造成浏览器冻结,你也许会认为以前的那些问题根本就没有解决。不过你不必耽心,Concurrent.Thread提供了一个应用JavaScript 的异步通信方式实现的定制通信库,它被设计成当一个线程在等待服务器的响应时允许其它线程运行。这个通信库存于 Concurrent.Thread.Http下。它的用法如下所示:
复制代码 代码如下:




get()方法,就像它的名字暗示的那样,可以通过HTTP的GET方法获得指定URL的内容,它将目标URL作为第一个参数,将一个代表HTTP请求头的数组作为可选的第二个参数。get()方法与服务器交互,当得到服务器的响应后就返回一个XMLHttpRequest对象作为返回值。当get()方法返回时,已经收到了服务器响应,所以就没必要再用回调函数接收结果。自然,也不必再耽心当程序等待服务器的响应时浏览器冻结的情况了。另外,还有一个 post()方法可以用来发送数据到服务器:
复制代码 代码如下:




post()方法将目的URL作为第一个参数,要发送的内容作为第二个参数。像get()方法那样,你也可以将请求头作为可选的第三个参数。

如果你用这个通信库实现了第一个例子当中的getArticle()方法,那么你很快就能应用文章开头示例的那种简单的方法写出getArticleWithCache(),backgroundLoad ()以及其它调用了getArticle()方法的函数了。即使是那版backgroundLoad()正在读文章数据,照例还有另外一个线程可以对用户请求做出响应,浏览器因此也不会冻结。现在,你能理解在JavaScript中应用多线程有多实用了?

想了解更多
我向你介绍了一个可以在JavaScript中应用多线程的库:Concurrent.Thread。这篇文章的内容只是很初级的东西,如果你想更深入的了解,我推荐您去看the tutorial。它提供有关Concurrent.Thread用法的更多内容,并列出了可供高级用户使用的文档,是最适合起步的材料。访问他们的网站也不错,那里提供更多信息。

有关作者
Daisuke Maki:从International Christian大学文科学院自然科学分部毕业后(取得文学学士学位),又在Electro-Communications大学的研究生院信息专业攻读硕士学位。擅长Web开发和应用JavaScript的AJAX。他开发了Concurrent.Thread。2006财政年度在日本信息技术促进机构(IPA)指导的项目Explatory Software Project中应用了这个设计。

目前已经拥有一个工学硕士学位的他正在Electro-Communications大学的研究生院注册攻读博士学位。

相关文章

java速学教程(入门到精通)
java速学教程(入门到精通)

java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
Golang 网络安全与加密实战
Golang 网络安全与加密实战

本专题系统讲解 Golang 在网络安全与加密技术中的应用,包括对称加密与非对称加密(AES、RSA)、哈希与数字签名、JWT身份认证、SSL/TLS 安全通信、常见网络攻击防范(如SQL注入、XSS、CSRF)及其防护措施。通过实战案例,帮助学习者掌握 如何使用 Go 语言保障网络通信的安全性,保护用户数据与隐私。

1

2026.01.29

俄罗斯Yandex引擎入口
俄罗斯Yandex引擎入口

2026年俄罗斯Yandex搜索引擎最新入口汇总,涵盖免登录、多语言支持、无广告视频播放及本地化服务等核心功能。阅读专题下面的文章了解更多详细内容。

420

2026.01.28

包子漫画在线官方入口大全
包子漫画在线官方入口大全

本合集汇总了包子漫画2026最新官方在线观看入口,涵盖备用域名、正版无广告链接及多端适配地址,助你畅享12700+高清漫画资源。阅读专题下面的文章了解更多详细内容。

137

2026.01.28

ao3中文版官网地址大全
ao3中文版官网地址大全

AO3最新中文版官网入口合集,汇总2026年主站及国内优化镜像链接,支持简体中文界面、无广告阅读与多设备同步。阅读专题下面的文章了解更多详细内容。

244

2026.01.28

php怎么写接口教程
php怎么写接口教程

本合集涵盖PHP接口开发基础、RESTful API设计、数据交互与安全处理等实用教程,助你快速掌握PHP接口编写技巧。阅读专题下面的文章了解更多详细内容。

8

2026.01.28

php中文乱码如何解决
php中文乱码如何解决

本文整理了php中文乱码如何解决及解决方法,阅读节专题下面的文章了解更多详细内容。

13

2026.01.28

Java 消息队列与异步架构实战
Java 消息队列与异步架构实战

本专题系统讲解 Java 在消息队列与异步系统架构中的核心应用,涵盖消息队列基本原理、Kafka 与 RabbitMQ 的使用场景对比、生产者与消费者模型、消息可靠性与顺序性保障、重复消费与幂等处理,以及在高并发系统中的异步解耦设计。通过实战案例,帮助学习者掌握 使用 Java 构建高吞吐、高可靠异步消息系统的完整思路。

10

2026.01.28

Python 自然语言处理(NLP)基础与实战
Python 自然语言处理(NLP)基础与实战

本专题系统讲解 Python 在自然语言处理(NLP)领域的基础方法与实战应用,涵盖文本预处理(分词、去停用词)、词性标注、命名实体识别、关键词提取、情感分析,以及常用 NLP 库(NLTK、spaCy)的核心用法。通过真实文本案例,帮助学习者掌握 使用 Python 进行文本分析与语言数据处理的完整流程,适用于内容分析、舆情监测与智能文本应用场景。

24

2026.01.27

拼多多赚钱的5种方法 拼多多赚钱的5种方法
拼多多赚钱的5种方法 拼多多赚钱的5种方法

在拼多多上赚钱主要可以通过无货源模式一件代发、精细化运营特色店铺、参与官方高流量活动、利用拼团机制社交裂变,以及成为多多进宝推广员这5种方法实现。核心策略在于通过低成本、高效率的供应链管理与营销,利用平台社交电商红利实现盈利。

124

2026.01.26

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Excel 教程
Excel 教程

共162课时 | 14.1万人学习

Java 教程
Java 教程

共578课时 | 52.8万人学习

HTML教程
HTML教程

共500课时 | 5.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号