异步javascript的原理和实现

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

我的答案当然是”能”。

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

编程平台选择没花费时间,直接选定基于Greasemonkey写自己的脚本,浏览器当然是firefox了。脚本的工作过程:

这里的技术难点在于:

如果我是菜鸟的话,我当然直接写一个类似这样的应用逻辑:

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

为了满足”不锁死”的要求,我们可以把脚本修改成这样:

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

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

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

异步操作的难点在两个地方:

最简单的实现当然就是异步循环了,我的实现代码如下:

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

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

对于非等待并执行的逻辑,简单一个 setTimeout 就可以了。

异步容易,实现异步中的顺序才叫难度呢。最早的起因是我要实现3步,但是第二部是一个异步的100多次的循环。也就是说,我要实现的3步操作,其实是103次的顺序异步操作。为了一个如何在浏览器中实现可响应的等待,找破了脑袋,只找到一个firefox中的实现,还要申请特权调用。

最后想出了一个简单的方法,就是引入了”执行链(Execution Chain)”的概念,同一个执行链的所有登记函数是顺序的,不同执行链之间没有任何关系。另外,不提供互斥(mutex)等概念,如果要同步,自行在代码中检查。

在同一个执行链中,保存一个执行令牌,只有令牌和函数序号匹配,才允许执行,这样就保证了异步执行的顺序性。

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

1:      function testAsync()
2:      {
3:          asyncSeq([function(){println("aSyncSeq -0 ");}
4:              , function(){println("aSyncSeq -1 ");}
5:              , function(){println("aSyncSeq -2 ");}
6:              , function(){println("aSyncSeq -3 ");}
7:              , function(){println("aSyncSeq -4 ");}
8:              , function(){println("aSyncSeq -5 ");}
9:              , function(){println("aSyncSeq -6 ");}
10:              , function(){println("aSyncSeq -7 ");}
11:              , function(){println("aSyncSeq -8 ");}
12:              , function(){println("aSyncSeq -9 ");}
13:              , function(){println("aSyncSeq -10 ");}
14:              , function(){println("aSyncSeq -11 ");}
15:              , function(){println("aSyncSeq -12 ");}
16:              , function(){println("aSyncSeq -13 ");}
17:              , function(){println("aSyncSeq -14 ");}
18:              , function(){println("aSyncSeq -15 ");}
19:              , function(){println("aSyncSeq -16 ");}
20:              , function(){println("aSyncSeq -17 ");}
21:              , function(){println("aSyncSeq -18 ");}
22:              , function(){println("aSyncSeq -19 ");}
23:              , function(){println("aSyncSeq -20 ");}
24:              , function(){println("aSyncSeq -21 ");}
25:              , function(){println("aSyncSeq -22 ");}
26:              , function(){println("aSyncSeq -23 ");}
27:              , function(){println("aSyncSeq -24 ");}
28:              , function(){println("aSyncSeq -25 ");}
29:              , function(){println("aSyncSeq -26 ");}
30:              , function(){println("aSyncSeq -27 ");}
31:              , function(){println("aSyncSeq -28 ");}
32:              , function(){println("aSyncSeq -29 ");}
33:          ]);
34:
35:          asyncSeq([function(){println("aSyncSeq test-chain -a0 ");}
36:              , function(){println("aSyncSeq test-chain -a1 ");}
37:              , function(){println("aSyncSeq test-chain -a2 ");}
38:              , function(){println("aSyncSeq test-chain -a3 ");}
39:              , function(){println("aSyncSeq test-chain -a4 ");}
40:              , function(){println("aSyncSeq test-chain -a5 ");}
41:              , function(){println("aSyncSeq test-chain -a6 ");}
42:              , function(){println("aSyncSeq test-chain -a7 ");}
43:              , function(){println("aSyncSeq test-chain -a8 ");}
44:          ], "test-chain");
45:
46:          asyncSeq([function(){println("aSyncSeq -a0 ");}
47:              , function(){println("aSyncSeq -a1 ");}
48:              , function(){println("aSyncSeq -a2 ");}
49:              , function(){println("aSyncSeq -a3 ");}
50:              , function(){println("aSyncSeq -a4 ");}
51:              , function(){println("aSyncSeq -a5 ");}
52:              , function(){println("aSyncSeq -a6 ");}
53:              , function(){println("aSyncSeq -a7 ");}
54:              , function(){println("aSyncSeq -a8 ");}
55:          ]);
56:      }
57:
58:      var textArea = null;
59:
60:      function println(text)
61:      {
62:          if( textArea == null )
63:          {
64:              textArea = document.getElementById("text");
65:              textArea.value = "";
66:          }
67:
68:          textArea.value = textArea.value + text + "\r\n";
69:      }

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

Original: https://www.cnblogs.com/BigTall/archive/2012/11/08/2759768.html
Author: 老翅寒暑
Title: 异步javascript的原理和实现

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/536567/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球