找回密码
 FreeOZ用户注册
查看: 3126|回复: 36
打印 上一主题 下一主题

[IT技术] 在JavaScript中模拟Thread,解决callback hell. 代码已github

[复制链接]
跳转到指定楼层
1#
发表于 24-7-2015 14:02:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
提示: 作者被禁止或删除, 无法发言

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?FreeOZ用户注册

x
本帖最后由 black_zerg 于 28-7-2015 18:03 编辑

最近写了段代码,感觉挺有意思也比较实用,分享一下。欢迎大家发表意见。

例子在这里! 请猛击
http://shen.apphb.com/jt/demo/core.html

https://github.com/shenAwesome/jThread

简介:
A poor man's Thread for JavaScript

The goal of jThread is to mimic the JAVA Thread in JavaScript to solve the 'Callback Hell' problem. In the Web page development, there are mainly 2 types of async logic: Ajax call or UI logic needs user interaction.
With callbacks, the code is often tightly coupled, the callback chain can be difficult to understand or debug. Code re-usability is harder to achieve . jThread utilizes the 'Thread&Activity' concept to solve the problem.
It is inspired by the workflow/activity design.

When coding with jThread, There are two important Types: Thread and Activity. A Thread mimics the behaviour of a thread in languages like Java or C#, where you can code in a blocking style without callbacks.
Activities are wrappers around the asynchronous functions and should only be used inside a thread. You can create new Activities easily to reuse them later, or just wrap any async logic as anonymous Activity in the Thread.

Rule of thumb:

1) Only use Activities inside a Thread.
2) Only Activities can interact with variables outside the thread safely. Use existing Activities or wrap the logic in an Activity.
3) An async function needs to have callback as the last parameter when wrapped as Activity. Callback(value) becomes the return value of the Activity.
4) You can create new Activities to reuse them later.

Imaging this requirement :

1) ajax call to a server
2) display return json on the page
3) user select some items

Below is the implementation in jThread.

  1. var $ = window.jThread;
  2. //With 2 functions as parameter, a Thread is  started.
  3. $(function () {
  4.     //built in functions(Activities),safe to use inside Thread
  5.     $.log('start');
  6.     $.sleep(1000);
  7.     var repos = $.getJson('https://api.github.com/users/shenAwesome/repos');
  8.     $.log(repos);
  9.     //with 1 function as parameter, an Activity is executed.
  10.     //When interacting with the 'outside world', wrap logic in an activity
  11.     $(function () {
  12.         var html = repos.map(function (repo) {
  13.             return '<label><input type="checkbox" />' + repo.name + '</label></br>';
  14.         }).join(' ');
  15.         document.querySelector('#div1').innerHTML = html;
  16.     });
  17.     //This wrapped logic has a finish callback. When the button clicked, this activity finishes and the thread will continue.
  18.     $(function (finish) {
  19.         var btn = document.createElement('button');
  20.         btn.innerHTML = "Click ME";
  21.         document.querySelector('#div1').appendChild(btn);
  22.         btn.onclick = finish;
  23.     });
  24.     $.log('user clicked!');
  25.     return "all done";//now the Thread is finished.

  26. }, function (ret) {//called when the thread finishes
  27.     alert(ret);
  28. });
复制代码

评分

参与人数 2威望 +100 收起 理由
cais + 50 很给力!
ubuntuhk + 50 谢谢分享!

查看全部评分

回复  

使用道具 举报

2#
 楼主| 发表于 24-7-2015 18:07:16 来自手机 | 只看该作者
提示: 作者被禁止或删除, 无法发言
楼主好厉害 我好佩服 哈哈哈

评分

参与人数 1威望 +50 收起 理由
lyle_w + 50 你太有才了!

查看全部评分

回复  

使用道具 举报

3#
发表于 24-7-2015 18:58:41 | 只看该作者
That is great
回复  

使用道具 举报

4#
发表于 24-7-2015 20:14:41 | 只看该作者
楼主好厉害 我好佩服
不过用异常控制执行流程始终不是正道。
异步转同步方面很多人做过很多尝试,其中比较值得看看的还是wind.js。

评分

参与人数 1威望 +50 收起 理由
black_zerg + 50 谢谢分享!

查看全部评分

回复  

使用道具 举报

5#
 楼主| 发表于 25-7-2015 10:53:52 来自手机 | 只看该作者
提示: 作者被禁止或删除, 无法发言
谢谢 推荐 我去看看 要说正道还是 callback王道  以后会有 async await . 但等不了。 promise只能算凑合 并不好看
回复  

使用道具 举报

6#
 楼主| 发表于 25-7-2015 10:57:43 来自手机 | 只看该作者
提示: 作者被禁止或删除, 无法发言
我看了一下windjs 觉得略复杂 我这代码很短 核心代码不超过百行, 只暴露一个名字即可, 类似 jQuery. 既然是 hack 就得简单不然 point在哪里
回复  

使用道具 举报

7#
 楼主| 发表于 25-7-2015 11:00:40 来自手机 | 只看该作者
提示: 作者被禁止或删除, 无法发言
我的想法是所有的异步方法都可以转同步 之后就能在 thread 里复用。 这个是我的应用场景比较需要的。我以前琢磨过 工作流 后来觉得太麻烦放弃了 个人搞点代码就怕麻烦 时间长自己都记不住
回复  

使用道具 举报

8#
 楼主| 发表于 25-7-2015 11:03:57 来自手机 | 只看该作者
提示: 作者被禁止或删除, 无法发言
感谢楼上两位的关注 实话说我写这些都是兴趣和工作关系不大 就是觉得比较好玩 没人探讨也寂寞
回复  

使用道具 举报

9#
 楼主| 发表于 25-7-2015 11:04:54 来自手机 | 只看该作者
提示: 作者被禁止或删除, 无法发言
下一步就是整理文档 做例子 我觉得这个东西还是很好使得 重点就是简单 直观
回复  

使用道具 举报

10#
发表于 25-7-2015 11:05:59 | 只看该作者
black_zerg 发表于 25-7-2015 11:04
下一步就是整理文档 做例子 我觉得这个东西还是很好使得 重点就是简单 直观

支持你
回复  

使用道具 举报

11#
 楼主| 发表于 25-7-2015 11:08:03 来自手机 | 只看该作者
提示: 作者被禁止或删除, 无法发言
真的要说王道 现在就是 callback和 promise 以后会有 generator和 async wait 我写这个的目的就是解决当下的问题 远的或者复杂的 一时也用不起来
回复  

使用道具 举报

12#
 楼主| 发表于 25-7-2015 11:37:04 来自手机 | 只看该作者
提示: 作者被禁止或删除, 无法发言
这里我把需要用户输入的逻辑看作异步方法 比如显示一个表单,等用户提交 这就是这个的核心目的 把显示一块界面和 ajax等都包装成可复用的方法
回复  

使用道具 举报

13#
发表于 25-7-2015 13:40:46 | 只看该作者
读了code,感觉LZ的想法很有趣。但给泼冷水了——虽然从用户体验上实现了sync,但效率很低啊。跟promises这种面向workflow control的framework相比,我认为没有可能商用。

我认为还是应该尝试转变思想,适应async programming,因为这种async思想不但是适应JavaScript,还适应于cloud分布式管理(不管是C#还是Java,Cloud的Architecture通信管理可都要是async的啊)。

评分

参与人数 1威望 +50 收起 理由
black_zerg + 50 谢谢分享!

查看全部评分

回复  

使用道具 举报

14#
 楼主| 发表于 25-7-2015 15:06:29 | 只看该作者
提示: 作者被禁止或删除, 无法发言
本帖最后由 black_zerg 于 25-7-2015 15:43 编辑

感谢楼上的意见。

至于效率,可以说这个就根本就不在考虑之中。 我做这个基本上就是用于浏览器,不用于nodejs. 设想一下界面,差几毫秒根本不是个事。至于效率到底如何,理论上和callback的唯一差别就是 这里callback的执行会到下一个loop, 其他内部消耗应该是基本没有的。但是因为如前所述的设计场景,我也不准备做benchmark之类。

callback是javascript的自然形态,这没有什么争议。javascript的下几个版本在语言也会支持async 等语法。但是因为javascript的本质特点,估计thread是遥遥无期的。
之所以不走promise之类,就是因为那个看起来麻烦。then来then去的,未见得多直观。还有一些其他的库,都是比较麻烦,引入新语法。相对的,我这个小工具只有一个方法。给两个参数,就开始一个线程,给一个参数,就包装成activity,基本没有记忆成本

当然了,我觉得最直观的说明这个工具用途的办法,就是设计个比较复杂的引用场景,我们写几个例子 对比。

我对callback其实是没什么意见的,如果只有一两个callback都不是事。麻烦的就是比如先ajax,然后用户输入,然后根据用户输入再有不同的动作,再ajax,再有逻辑判断。然后用户还可以中断这个流程。 类似workflow. 这是我自己个人项目里的需求。我想过workflow引擎什么的但觉得太麻烦。
回复  

使用道具 举报

15#
 楼主| 发表于 25-7-2015 15:15:06 | 只看该作者
提示: 作者被禁止或删除, 无法发言
本帖最后由 black_zerg 于 25-7-2015 15:19 编辑

我再举个简单的应用,比如说css动画效果。因为可以用transit,所以我们可以直接把一个div居中,然后把opacity设1,然后设0,闪烁个几下。 现在问题来了,在普通javascript里没有办法sleep。用这个就可以直接设css然后sleep等transit完成。代码必然简短好维护。  这个的思路就是小工具,快速dirty。Javascript并不支持thread, 这百行代码也不可能无中生有,其实就是用callback模拟thread,省点眼睛而已
回复  

使用道具 举报

16#
发表于 25-7-2015 15:51:57 | 只看该作者
black_zerg 发表于 25-7-2015 15:06
感谢楼上的意见。

至于效率,可以说这个就根本就不在考虑之中。 我做这个基本上就是用于浏览器,不用于n ...

我对callback是将它分成独立的函数,这样一来,读起来非常清晰

评分

参与人数 1威望 +50 收起 理由
black_zerg + 50 谢谢分享!

查看全部评分

回复  

使用道具 举报

17#
 楼主| 发表于 25-7-2015 16:08:23 | 只看该作者
提示: 作者被禁止或删除, 无法发言
楼上不妨试试这个小工具,试试就知道。 我的表达能力也是有限,一直都是更喜欢用代码说明问题。或者你有段什么callback代码我给你转成用这个工具的形式
回复  

使用道具 举报

18#
发表于 25-7-2015 21:33:31 | 只看该作者
黑眼很牛呀。

评分

参与人数 1威望 +50 收起 理由
black_zerg + 50 我很赞同!哈哈

查看全部评分

回复  

使用道具 举报

19#
发表于 25-7-2015 23:45:57 | 只看该作者
JavaScript的一大特点就是没有thread,去掉了很多concurrency相关的问题。
对Java/C#比较熟的刚开始读JS代码的时候都是心惊胆战的,怎么一个个变量就这样随便更改,不用考虑多线程。
后来习惯了才好一点。
这要是加入了thread的支持,很多程序都要重写了。

评分

参与人数 1威望 +50 收起 理由
black_zerg + 50 谢谢分享!

查看全部评分

回复  

使用道具 举报

20#
 楼主| 发表于 26-7-2015 21:38:54 | 只看该作者
提示: 作者被禁止或删除, 无法发言
本帖最后由 black_zerg 于 26-7-2015 21:45 编辑

感谢楼上意见。 我对callback没有任何反感,只要套的不多,closure这些我用的顺手得很,这个工具也就是这么实现的。

但是想象一下你有十来个连续的async的操作,其中有各种判断逻辑而不是简单的按顺序进行,组织代码的策略就很重要。在我的审美观里,两三个callback嵌套就是极限,维护个10层callback就是悲剧了。

我写这个的动机来自于我自己个一个个人项目。 想象一下你有一个工具条,每个按钮 点了之后做不同的事情。每个按钮都打开一个工作流程一样的东西,流程可能在中途中断,比如用户按别的按钮就又开始一个工作流程,系统必须把之前的流程清空(因为前流程可能修改了界面)。不知道我这个解释的是否明白。我之前试过写工作流引擎,太复杂放弃了。现在目前计划就用这个。 这个只是十几行代码而已,所以我都不会叫他库,只是个小工具。

这里的线程并不是要真正的线程,也并不会真的blocking,也没有访问冲突的问题,实际上还是callback turn base。  技术上可以看成工作流,但是代码形式上类似Thread.这样基本没什么学习记忆成本。 但是cais说得很对的,就是用这个必须遵守几个读写变量的规则,我得写好文档。我觉得现在这个文档怎么写好点就很挑战,英文感觉也不够用。下周有时间就再更新文档,欢迎大家对文档提意见。

如何沟通合作也真的蛮挑战的。很感谢大家的关注。请大家多批评,我试着回答。
回复  

使用道具 举报

21#
 楼主| 发表于 26-7-2015 21:50:35 | 只看该作者
提示: 作者被禁止或删除, 无法发言
本帖最后由 black_zerg 于 26-7-2015 22:06 编辑
  1. //外部逻辑   普通scope

  2. $(function(){
  3.     //工作流逻辑,不能访问外部变量,除非 常量
  4.     var value;
  5.     //控制交给activity.
  6.     value = $(function(){
  7.            //在 activity里 可以读写 外部变量, 但只能通过return写工作流 变量,
  8.     });
  9.     //activity完成了,控制又回到Thread, 变成工作流逻辑
  10.     if (value >0){
  11.          
  12.     }
  13.      return value;
  14. },function(value){
  15.       //线程结束的逻辑,可以访问外部变量
  16. });
复制代码


大概是这么个意思。请大家看看是不是 makes sense。另外这个词汇和意思如何解释才清楚,请大家指正。

功能上我准备得支持个goto啥的,另外线程得支持 restart,cancel ,暂停和继续好像没什么 实际用例,也比较难实现。
回复  

使用道具 举报

22#
发表于 28-7-2015 02:13:00 | 只看该作者
大概把代码读了一遍,又step through了你的例子,大概了解你的想法。
基本思路是每一步(activity)的结果都是被memoize的,跑过一次,就不会再跑了。
然后跑完一步,就通过throw exception的方式结束当前的运行,等待async的结果出来,再重新从第一步开始跑。
因为前面跑过的都已经memoized了,所以前面的Activity都不需要再跑一次,直接就会开始跑下面还没开始跑的那一步。
不知道我的理解对不对。
这个思路挺新颖的。会多花点时间在重复运行一些东西,但是总体来说效率应该不会太低。步数不多的话,应该没问题。
目前我还不是很理解怎么样重置回去以前的某一步,没找到throw 数字的那个分支。是不是你说的goto支持?
另外就是这整个是单一流程,重复是靠最后的onFinish。但是没办法if/else,可能加了goto支持之后有可能实现。
如果要支持两个event handlers,onclick onfocus之类的,怎么做?
工作流里面,好像不能出现任何有side effect的东西,比如alert,不然就会被运行n次。
另外一个问题,就是,那些被包装起来的callback,就是那个finish里面的setTimeout里面的function,被强制在下一个tick里面运行。
这样的话,用来做event handler的时候,会有限制。有些东西,只能在跟event handler同一个tick里面运行,比如window.open, request full screen等。
这个问题不知道lz是怎么解决的。还是说我的理解有错误。
isHandle应该作isHandled比较好。
给lz参考一下。
回复  

使用道具 举报

23#
发表于 28-7-2015 02:15:49 | 只看该作者
感觉lz想实现的东西,在新版本的yield支持出来之后,应该可以比较容易的实现。
回复  

使用道具 举报

24#
 楼主| 发表于 28-7-2015 08:12:00 来自手机 | 只看该作者
提示: 作者被禁止或删除, 无法发言
gernator 之类我并没什么深刻认识因为没实际用过 楼上不妨分享一下 我要实现的就是类似于thread的语法, 也就是最直观的程序流程控制,目的就是让这些逻辑一目了然。跳来跳去很容易晕
回复  

使用道具 举报

25#
 楼主| 发表于 28-7-2015 08:13:48 来自手机 | 只看该作者
提示: 作者被禁止或删除, 无法发言
我昨天又把代码用 typescript类写了一个版本 加了 label goto 支持 还在测试 不过目前感觉还是不错的 这个 goto的作用就是比如用户输入了啥 服务器验证不对,你要跳回到之前某一步 我觉得蛮实用
回复  

使用道具 举报

26#
 楼主| 发表于 28-7-2015 08:18:54 来自手机 | 只看该作者
提示: 作者被禁止或删除, 无法发言
是的cais很厉害 这东西就是这么实现的 我那代码其实有点乱而且不是 oop而是 clousure风格 楼上解读的已经差不多了. 你说的没错那些有限制的操作只能在用户直接操作里触发。这个情况我这个基本解决不了,只要你牵涉到 async就没法解决即使用 callbak,这个我估计无解
回复  

使用道具 举报

27#
 楼主| 发表于 28-7-2015 08:21:06 来自手机 | 只看该作者
提示: 作者被禁止或删除, 无法发言
Throw 数字回去就会把数字看作步数 把这个数字之后的所有状态清空 那么就 goto了
回复  

使用道具 举报

28#
 楼主| 发表于 28-7-2015 08:21:47 来自手机 | 只看该作者
提示: 作者被禁止或删除, 无法发言
但我昨天已经写了方法 这样可以支持 goto到一个 label不然谁知道 这个步数是什么
回复  

使用道具 举报

29#
 楼主| 发表于 28-7-2015 08:23:45 来自手机 | 只看该作者
提示: 作者被禁止或删除, 无法发言
这个是可以 if else 的 这个其实是最重要的功能 不然我也不做这个了  那这个基本毛用也没有我也不分享了
回复  

使用道具 举报

30#
 楼主| 发表于 28-7-2015 08:26:23 来自手机 | 只看该作者
提示: 作者被禁止或删除, 无法发言
主要的限制在于 变量访问上 如 cais 说的避免 重复执行 这个就得遵守些简单的准则 除此就是标准 JavaScript没有什么限制 if else while 什么的我看不出来有什么问题
回复  

使用道具 举报

您需要登录后才可以回帖 登录 | FreeOZ用户注册

本版积分规则

小黑屋|手机版|Archiver|FreeOZ论坛

GMT+10, 29-4-2024 00:22 , Processed in 0.067102 second(s), 45 queries , Gzip On, Redis On.

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表