异步javascript的原理和实现技巧介绍_javascript技巧
因为工作的需要,我要在网页端编写一段脚本,把数据通过网页批量提交到系统中去。所以我就想到了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。

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

热门话题

华为手机如何实现双微信登录?随着社交媒体的兴起,微信已经成为人们日常生活中不可或缺的沟通工具之一。然而,许多人可能会遇到一个问题:在同一部手机上同时登录多个微信账号。对于华为手机用户来说,实现双微信登录并不困难,本文将介绍华为手机如何实现双微信登录的方法。首先,华为手机自带的EMUI系统提供了一个很便利的功能——应用双开。通过应用双开功能,用户可以在手机上同

nohup的作用及原理解析在Unix和类Unix操作系统中,nohup是一个常用的命令,用于在后台运行命令,即便用户退出当前会话或关闭终端窗口,命令仍然能够继续执行。在本文中,我们将详细解析nohup命令的作用和原理。一、nohup的作用后台运行命令:通过nohup命令,我们可以让需要长时间运行的命令在后台持续执行,而不受用户退出终端会话的影响。这在需要运行

编程语言PHP是一种用于Web开发的强大工具,能够支持多种不同的编程逻辑和算法。其中,实现斐波那契数列是一个常见且经典的编程问题。在这篇文章中,将介绍如何使用PHP编程语言来实现斐波那契数列的方法,并附上具体的代码示例。斐波那契数列是一个数学上的序列,其定义如下:数列的第一个和第二个元素为1,从第三个元素开始,每个元素的值等于前两个元素的和。数列的前几个元

如何在华为手机上实现微信分身功能随着社交软件的普及和人们对隐私安全的日益重视,微信分身功能逐渐成为人们关注的焦点。微信分身功能可以帮助用户在同一台手机上同时登录多个微信账号,方便管理和使用。在华为手机上实现微信分身功能并不困难,只需要按照以下步骤操作即可。第一步:确保手机系统版本和微信版本符合要求首先,确保你的华为手机系统版本已更新到最新版本,以及微信App

在当今的软件开发领域中,Golang(Go语言)作为一种高效、简洁、并发性强的编程语言,越来越受到开发者的青睐。其丰富的标准库和高效的并发特性使它成为游戏开发领域的一个备受关注的选择。本文将探讨如何利用Golang来实现游戏开发,并通过具体的代码示例来展示其强大的可能性。1.Golang在游戏开发中的优势作为一种静态类型语言,Golang在构建大型游戏系统

PHP游戏需求实现指南随着互联网的普及和发展,网页游戏的市场也越来越火爆。许多开发者希望利用PHP语言来开发自己的网页游戏,而实现游戏需求是其中一个关键步骤。本文将介绍如何利用PHP语言来实现常见的游戏需求,并提供具体的代码示例。1.创建游戏角色在网页游戏中,游戏角色是非常重要的元素。我们需要定义游戏角色的属性,比如姓名、等级、经验值等,并提供方法来操作这些

并发和异步编程并发编程处理同时执行的多个任务,异步编程是一种并发编程,其中任务不会阻塞线程。asyncio是python中用于异步编程的库,它允许程序在不阻塞主线程的情况下执行I/O操作。事件循环asyncio的核心是事件循环,它监控I/O事件并调度相应的任务。当一个协程准备就绪时,事件循环会执行它,直到它等待I/O操作。然后,它会暂停协程并继续执行其他协程。协程协程是可暂停和恢复执行的函数。asyncdef关键字用于创建协程。协程使用await关键字等待I/O操作完成。asyncio的基础以下

目录Astar Dapp 质押原理质押收益 拆解潜在空投项目:AlgemNeurolancheHealthreeAstar Degens DAOVeryLongSwap 质押策略 & 操作“AstarDapp质押”今年初已升级至V3版本,对质押收益规则做了不少调整。目前首个质押周期已结束,第二质押周期的“投票”子周期刚开始。要获取“额外奖励”收益,需把握此关键阶段(预计持续至6月26日,现余不到5天)。我将细致拆解Astar质押收益,
