0

0

异步javascript的原理和实现技巧介绍_javascript技巧

php中文网

php中文网

发布时间:2016-05-16 17:48:38

|

1311人浏览过

|

来源于php中文网

原创

因为工作的需要,我要在网页端编写一段脚本,把数据通过网页批量提交到系统中去。所以我就想到了Greasemonkey插件,于是就开始动手写,发现问题解决得很顺利。但是在对脚本进行总结和整理的时候,我习惯性地问了自己一个问题:能不能再简单点?
我的答案当然是“能”。

首先回顾我的数据批量提交的需求:我有一批用户数据要插入到系统中,但是因为系统库表结构不是行列式的,所以无法转化为sql语句插入。要插入的数据有接近200条,就是傻呵呵地手工录入到系统,估计也要1天的时间。作为程序员,当然不会干这么傻的事情,我一定要用程序来解决。这个编程的过程耗费了我1天的时间。相比手工录入,我额外收入是这篇博文,绝对的合算!

编程平台选择没花费时间,直接选定基于Greasemonkey写自己的脚本,浏览器当然是firefox了。脚本的工作过程:
在脚本中预先存放要插入的数据
模拟鼠标点击,打开页面中的输入窗口
将数据录入到输入窗口,并模拟点击“提交”按钮,将数据提交到系统中。
依次循环,直到所有数据都处理完毕。

这里的技术难点在于:
打开输入窗口,需要等待不定期的时间,视网络情况而定。
提交数据到后台,需要等待处理完毕之后才可以循环下一个数据。
如果我是菜鸟的话,我当然直接写一个类似这样的应用逻辑:

复制代码 代码如下:

for(var i = 0; i { 3: clickButtonForInputWindow();
waitInputWindow();
enterInputData(dataArray[i]);
clickSubmitButton();
waitInputWindowClose();
}

实际上这样写所有浏览器都会陷入一片白屏,并在若干分钟之后提示“没有响应”而被强行终止掉。原因就是浏览器在调用javascript的时候,主界面是停止响应的,因为cpu交给js执行了,没有时间去处理界面消息。

为了满足“不锁死”的要求,我们可以把脚本修改成这样:
复制代码 代码如下:

for(var i = 0; i {
setTimeout(clickButtonForInputWindow);

setTimeout(waitInputWindowClose);
}

实际上setTimeout和setInterval是浏览器唯一可以支持异步的操作。如何更优雅地使用这两个函数来实现异步操作呢?目前简单的答案是老赵的Wind.js。虽然我没有用过这个函数库,但是光是$await调用,就是符合我一贯对简洁的要求的。但是对于我这样的单个文件的脚本来说,去网上下载一个外部js库,明显不如有一段支持异步操作的代码拷贝过来的快和爽。

所以我决定另辟蹊径,做一个不要编译而且易用性还可以更能够Copy&Paste的异步函数库。

说异步之前,我们一起回忆一下同步操作的几种结构类型:

顺序:就是语句的先后顺序执行
判断:就是判断语句
循环:严格来说应该是跳转(goto),但大多数现代语言都取消了goto。循环其实应该是复合结构,是if和goto的组合体。
异步操作的难点在两个地方:

异步的判断:异步情况下的判断基本都是检测条件十分满足,然后执行某些动作。
异步的顺序:顺序中的每一步操作之后都要交回控制权,等待在下一个时间片中继续执行下一步。难点是如何保持顺序性。尤其在两个顺序动作中间夹杂一个异步的循环的时候。
异步的循环:每次循环之后都交回控制权到浏览器,如此循环,直到运行结束。
最简单的实现当然就是异步循环了,我的实现代码如下:
复制代码 代码如下:

function asyncWhile(fn, interval)
{
if( fn == null || (typeof(fn) != "string" && typeof(fn) != "function") )
return;
var wrapper = function()
{
if( (typeof(fn) == "function" ? fn() : eval(fn) ) !== false )
setTimeout(wrapper, interval == null? 1: interval);
}
wrapper();
}

核心内容就是:如果fn函数返回值不是false,就继续下一个setTimeout的登记调用。

实际上,“等待并执行”逻辑,根本上就是一个异步循环问题。这种情况的实现方法示例如下:
复制代码 代码如下:

asyncWhile(function(){
if( xxxCondition == false )
return true; // 表示继续循环
else
doSomeThing();
return false; // 表示不需要继续循环了
});

对于非等待并执行的逻辑,简单一个 setTimeout 就可以了。
异步容易,实现异步中的顺序才叫难度呢。最早的起因是我要实现3步,但是第二部是一个异步的100多次的循环。也就是说,我要实现的3步操作,其实是103次的顺序异步操作。为了一个如何在浏览器中实现可响应的等待,找破了脑袋,只找到一个firefox中的实现,还要申请特权调用。
最后想出了一个简单的方法,就是引入了“执行链(Execution Chain)”的概念,同一个执行链的所有登记函数是顺序的,不同执行链之间没有任何关系。另外,不提供互斥(mutex)等概念,如果要同步,自行在代码中检查。
在同一个执行链中,保存一个执行令牌,只有令牌和函数序号匹配,才允许执行,这样就保证了异步执行的顺序性。
复制代码 代码如下:

function asyncSeq(funcArray, chainName, abortWhenError)
{
if( typeof(funcArray) == "function" )
return asyncSeq([funcArray], chainName, abortWhenError);

if( funcArray == null || funcArray.length == 0 )
return;

if( chainName == null ) chainName = "__default_seq_chain__";
var tInfos = asyncSeq.chainInfos = asyncSeq.chainInfos || {};
var tInfo = tInfos[chainName] = tInfos[chainName] || {count : 0, currentIndex : -1, abort : false};

for(var i = 0; i {
asyncWhile(function(item, tIndex){
return function(){
if( tInfo.abort )
return false;
if( tInfo.currentIndex return true;
else if( tInfo.currentIndex == tIndex )
{
try{
item();
}
catch(e){
if( abortWhenError ) tInfo.abort = true;
}
finally{
tInfo.currentIndex ++;
}
}
else
{
if( abortWhenError ) tInfo.abort = true;
}
return false;
};
}(funcArray[i], tInfo.count ++));
}

setTimeout(function(){
if( tInfo.count > 0 && tInfo.currentIndex == -1 )
tInfo.currentIndex = 0;
},20); // 为了调试的原因,加了延迟启动
}

由此,一个支持Copy&Paste的异步js函数库就完成了。具体的使用例子如下:
复制代码 代码如下:

function testAsync()
{
asyncSeq([function(){println("aSyncSeq -0 ");}
, function(){println("aSyncSeq -1 ");}
, function(){println("aSyncSeq -2 ");}
, function(){println("aSyncSeq -3 ");}
, function(){println("aSyncSeq -4 ");}
, function(){println("aSyncSeq -5 ");}
, function(){println("aSyncSeq -6 ");}
, function(){println("aSyncSeq -7 ");}
, function(){println("aSyncSeq -8 ");}
, function(){println("aSyncSeq -9 ");}
, function(){println("aSyncSeq -10 ");}
, function(){println("aSyncSeq -11 ");}
, function(){println("aSyncSeq -12 ");}
, function(){println("aSyncSeq -13 ");}
, function(){println("aSyncSeq -14 ");}
, function(){println("aSyncSeq -15 ");}
, function(){println("aSyncSeq -16 ");}
, function(){println("aSyncSeq -17 ");}
, function(){println("aSyncSeq -18 ");}
, function(){println("aSyncSeq -19 ");}
, function(){println("aSyncSeq -20 ");}
, function(){println("aSyncSeq -21 ");}
, function(){println("aSyncSeq -22 ");}
, function(){println("aSyncSeq -23 ");}
, function(){println("aSyncSeq -24 ");}
, function(){println("aSyncSeq -25 ");}
, function(){println("aSyncSeq -26 ");}
, function(){println("aSyncSeq -27 ");}
, function(){println("aSyncSeq -28 ");}
, function(){println("aSyncSeq -29 ");}
]);

asyncSeq([function(){println("aSyncSeq test-chain -a0 ");}
, function(){println("aSyncSeq test-chain -a1 ");}
, function(){println("aSyncSeq test-chain -a2 ");}
, function(){println("aSyncSeq test-chain -a3 ");}
, function(){println("aSyncSeq test-chain -a4 ");}
, function(){println("aSyncSeq test-chain -a5 ");}
, function(){println("aSyncSeq test-chain -a6 ");}
, function(){println("aSyncSeq test-chain -a7 ");}
, function(){println("aSyncSeq test-chain -a8 ");}
], "test-chain");

asyncSeq([function(){println("aSyncSeq -a0 ");}
, function(){println("aSyncSeq -a1 ");}
, function(){println("aSyncSeq -a2 ");}
, function(){println("aSyncSeq -a3 ");}
, function(){println("aSyncSeq -a4 ");}
, function(){println("aSyncSeq -a5 ");}
, function(){println("aSyncSeq -a6 ");}
, function(){println("aSyncSeq -a7 ");}
, function(){println("aSyncSeq -a8 ");}
]);
}

var textArea = null;

function println(text)
{
if( textArea == null )
{
textArea = document.getElementById("text");
textArea.value = "";
}

textArea.value = textArea.value + text + "\r\n";
}

最后,要向大家说一声抱歉,很多只想拿代码的朋友恐怕要失望了,如果你真的不知道怎么处理这些多余的行号,你可以学习一下正则表达式的替换,推荐用UltraEdit。

相关文章

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数据库错误处理方法、技巧、管理策略相关内容,阅读专题下面的文章了解更多详细内容。

39

2026.02.06

java多线程方法汇总
java多线程方法汇总

本专题整合了java多线程面试题、实现函数、执行并发相关内容,阅读专题下面的文章了解更多详细内容。

17

2026.02.06

1688阿里巴巴货源平台入口与批发采购指南
1688阿里巴巴货源平台入口与批发采购指南

本专题整理了1688阿里巴巴批发进货平台的最新入口地址与在线采购指南,帮助用户快速找到官方网站入口,了解如何进行批发采购、货源选择以及厂家直销等功能,提升采购效率与平台使用体验。

289

2026.02.06

快手网页版入口与电脑端使用指南 快手官方短视频观看入口
快手网页版入口与电脑端使用指南 快手官方短视频观看入口

本专题汇总了快手网页版的最新入口地址和电脑版使用方法,详细提供快手官网直接访问链接、网页端操作教程,以及如何无需下载安装直接观看短视频的方式,帮助用户轻松浏览和观看快手短视频内容。

150

2026.02.06

C# 多线程与异步编程
C# 多线程与异步编程

本专题深入讲解 C# 中多线程与异步编程的核心概念与实战技巧,包括线程池管理、Task 类的使用、async/await 异步编程模式、并发控制与线程同步、死锁与竞态条件的解决方案。通过实际项目,帮助开发者掌握 如何在 C# 中构建高并发、低延迟的异步系统,提升应用性能和响应速度。

11

2026.02.06

Python 微服务架构与 FastAPI 框架
Python 微服务架构与 FastAPI 框架

本专题系统讲解 Python 微服务架构设计与 FastAPI 框架应用,涵盖 FastAPI 的快速开发、路由与依赖注入、数据模型验证、API 文档自动生成、OAuth2 与 JWT 身份验证、异步支持、部署与扩展等。通过实际案例,帮助学习者掌握 使用 FastAPI 构建高效、可扩展的微服务应用,提高服务响应速度与系统可维护性。

7

2026.02.06

JavaScript 异步编程与事件驱动架构
JavaScript 异步编程与事件驱动架构

本专题深入讲解 JavaScript 异步编程与事件驱动架构,涵盖 Promise、async/await、事件循环机制、回调函数、任务队列与微任务队列、以及如何设计高效的异步应用架构。通过多个实际示例,帮助开发者掌握 如何处理复杂异步操作,并利用事件驱动设计模式构建高效、响应式应用。

11

2026.02.06

java连接字符串方法汇总
java连接字符串方法汇总

本专题整合了java连接字符串教程合集,阅读专题下面的文章了解更多详细操作。

47

2026.02.05

java中fail含义
java中fail含义

本专题整合了java中fail的含义、作用相关内容,阅读专题下面的文章了解更多详细内容。

29

2026.02.05

热门下载

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

精品课程

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

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