- 主题:花了一天实现了一个协程库
https://github.com/hgoldfish/qtng-jsasync () => {
let msg = await event.wait();
output(msg);
let r = await qtng.http.get("/test-data.html");
if (r.isOk) {
output(r.data);
} else {
console.debug(r.error);
}
}
因为只是原型,所以 http get 这些支持不太好。目前只能简单 get/post. 不过基本的功能都有了。
简单示例代码:
https://github.com/hgoldfish/qtng-js/blob/main/app/main.js
具体实现代码只有一个文件,并且没有依赖,但要求 js 支持 Map, async/await
https://github.com/hgoldfish/qtng-js/blob/main/app/qtng.js
--
FROM 110.85.22.*
为了实现 Lock.
协程间通信很偶尔的情况会用到 Lock, Semaphore 和 Condition. 虽然很偶尔,但缺少这些同步机制的协程库都是不完整的。
其实 Lock 就是 jquery 的 Deferred,或者说 Promise,现在你也经常在用的吧。但协程库不能直接用 Deferred/Promise,因为协程 release 一个 Lock 之后,一般要等回事件循环以后才能跳到另外一个协程。所以要特别地提供一个 Lock. Lock 和 Semaphore 的实现方法一样,所以先实现一个 Semaphore.
Event 就是带值的 Lock, 如果无锁定,wait() 就直接返回值。否则加锁等待。set() 设置值并释放锁。上面那个帖子的实现其实不太对,github 上面的是针对协程的实现比较难懂。我这里换个写法给你看你就明白了:
class Event {
constructor() {
this.value = undefined;
this.deferred = new Deferred();
}
async wait() {
if (this.deferred) {
return await this.deferred.promise();
} else {
return this.value;
}
}
set(value) {
this.deferred = null;
this.value = value;
}
clear() {
this.deferred = new Deferred();
}
}
很简单对吧,也可以理解成只有一个槽的队列。大约再三十行代码就扩展成真正的 Queue 了。
【 在 pangwa (学门手艺,混口饭吃.) 的大作中提到: 】
: 别的先不说, 引入Semaphore, 是为了解决啥问题?
--
FROM 112.47.122.*
Event 和 Promise 的使用场景是一样的。我举个例子。
比如你有两个模块,第一个模块负责登录,第二个模块需要登录以后才能用。两个模块都是框架调用的,你自己没法控制它们的顺序。这时候,你一般会设置一个:
let loginEvent = new Promise(...);
第二个模块要加载数据之前先等待登录事件:
loginEvent.then(...);
这时候 Event 跟 Promise 是完全一样的。相互可以代替:
let loginEvent = new Event();
await loginEvent.wait();
Event 和 Promise 使用上唯一不同的地方可能是 Event 可以被 clear(). 我实现 Event 主要是 Promise 不能被直接用于协程。作为对比,协程版本的 Event 的 Promise.all() 和 Promise.any() 这样写:
class Event {
static async all(events) {
for (let event of events)
if (!(await event.wait())) return false;
return true;
}
static async any(events) {
for (let event of events)
if (await event.wait()) return true;
return false;
}
}
总之,用了协程以后,一切回归到传统的 javascript 过程式编程,不再需要回调函数。
【 在 pangwa (学门手艺,混口饭吃.) 的大作中提到: 】
: 别的先不说, 引入Semaphore, 是为了解决啥问题?
--
FROM 112.47.122.*
感觉你太扣细节了。。Semaphore/Lock/Event 这些都是工具类,而且还是偶尔才用得上的工具类。我写在里面只是为了丰富工具库的内容。你关心那些有什么用啊。
你要讨论也讨论点核心啊。比如我那个 Coroutine 类可以说是整个工具库的核心。有了 Coroutine 和 CoroutineGroup 管理协程,给协程包个容器,才能实现我之前所说的 kill() 协程。
模拟 generator,你是不是在搞笑。我用的是 js 自带的 async/await 语法啊。。如果你不懂 async/await,建议你去看看相关的文章。为什么 js 要引入这个语法,而不是用 generator —— 我上面已经演示了 coroutine 可以实现 generator,但 generator 却未必能实现 coroutine,这是 js 引入 async/await 语法的原因。
你说的那些程序库,都是回调又回调。跟我压根不是一个路数。
【 在 pangwa (学门手艺,混口饭吃.) 的大作中提到: 】
: 搞了半天你真的是在模拟generator啊?不说实现, 你确定它会更好用? ……
: 说实话我感觉你对js的一些库缺少了解,比如saga,rxjs,tj/co……
: 发自「今日水木 on iPhone X」
: ...................
--
修改:hgoldfish FROM 112.47.122.*
FROM 112.47.122.*
如果你问。。新的协程库会比 rxjs 强吗?没有的,亲。
其实我觉得 rxjs/loaddash/vue/react 这一票,也没比 jquery 强多少。大多数功能,jquery 都能做,只是写起来很怪异。
我前面已经说了 Lock 和 Deferred/Promise 等价。所以 coroutine 和 promise,其实只是编程范式的区别而已。没有功能的差别。
【 在 pangwa (学门手艺,混口饭吃.) 的大作中提到: 】
: 搞了半天你真的是在模拟generator啊?不说实现, 你确定它会更好用? ……
: 说实话我感觉你对js的一些库缺少了解,比如saga,rxjs,tj/co……
: 发自「今日水木 on iPhone X」
: ...................
--
FROM 110.85.22.*
我前面不说了吗?? generator 和 coroutine 的区别。
你就当做一个 async/await 版本的 saga 就行了。
【 在 pangwa (学门手艺,混口饭吃.) 的大作中提到: 】
: 仔细看看saga ……
: 发自「今日水木 on iPhone X」
--
修改:hgoldfish FROM 110.85.22.*
FROM 110.85.22.*
协程也是 js 的啊。总不能说用 promise/generator 才是真 js 程序员。不能说像我这种使用 coroutine 的是异端,要被吊死吧?
[].forEach(...) // 优雅!
for (let ... of ...) // 吊死!
promise.then().catch().finally() // 优雅!
try {await ...} catch() {} finally () // 吊死!
其实你说得也没错。我一开始就是想让大家给介绍一下协程库,奔着协程去的。说明我是偏好协程的。你翻翻上面我的帖子,看我是不是一直在说要求一个协程库。
【 在 pangwa (学门手艺,混口饭吃.) 的大作中提到: 】
: 你看 你说讨厌回调, saga就完全没有回调,至于你举的登录的例子,基于saga可以写的比你这个好看多了,无非是你觉得协程好就一定要在js中实现一个蹩脚的协程,实际上这个世界有其他更好的模式解决这些问题啊……
: 发自「今日水木 on iPhone X」
--
FROM 110.85.22.*
首先, generator 和 coroutine 都比 callback 更容易编写,这点你同意吧。至少 generator/corotuine 的写法能比 callback 少很多缩进等级。在我看来,js 社区现在流行两空格缩进简直是业界奇葩——callback 导致缩进太多是唯一的解释——五年前的 js 社区可不是这样的。
接下来你可以考虑一下 coroutine 为什么比 generator 先进。我前面说过 generator 有一些隐秘的缺陷。generator 是相对被动的,需要外部调用者调用 .next() 推动它的执行。
我再举一个例子:
funcion *top() { middle() }
function middle() { // call bottom() }
function *bottom() { yield fetch(...) }
这个调用顺序里面,其中的 middle() 不是 generator,为了调用 bottom,你要怎么写。。只好再把这个 bottom() 托管给 saga 这个容器对不对。
容器,说好听点是帮你管理状态,难听点就是程序员思维的牢笼。
而 coroutine 就没有这么麻烦了。直接调用,js 语言会帮你执行它。
所以我搞的这个 qtng 不说自己是框架,只说自己是工具库。你不用它,它就不存在。你用它也非常简单,import 进来,然后调用就行了,这是最简单的一个调用例子:
import { http } from "qtng"
(async () => {
let r = await http.get("/test-data.html");
console.debug(r.json);
})();
多简洁啊。。
【 在 pangwa (学门手艺,混口饭吃.) 的大作中提到: 】
: 好的好的,首先你说那一堆优雅和吊死的写法不是我的看法,我两种都用。
: 我一直在尝试理解你这么做的实际应用场景是什么(优于现有方案的点在哪),所以问了很多问题。如果只是想给大家介绍下协程0,0,好吧……
: 发自「今日水木 on iPhone X」
: ...................
--
FROM 110.85.22.*
反正我对那些框架没啥兴趣。
很好奇地问,js 不是函数式编程语言,没法从语法上面验证一个函数是纯的,没法规定一个对象是 immutable, 甚至连尾递归优化都没有。你们这些执着于这种虚妄的“先进”,何不考虑直接使用 ClosureScript 这些正规的函数式编程语言算了。
【 在 pangwa (学门手艺,混口饭吃.) 的大作中提到: 】
: 如果你理解saga的设计就不会这么说了,副作用的描述和执行分离是非常先进的理念。
: 发自「今日水木 on iPhone X」
--
FROM 110.85.22.*
这不是没找到么。。如果有人给我推荐一个好用的 js 协程库。我就不用浪费一个周末了。
话说,js 造协程库真是比其它语言简单多了。
【 在 pangwa (学门手艺,混口饭吃.) 的大作中提到: 】
: 一是js也没说的这么不堪,二也不是我一个人写代码啊。 搞工程不就是各种折衷么。 语言之争没啥意思,纯函数式语言也搞过,感觉还是过于阳春白雪了。
: 至于框架,saga我们在实际的工程中有过超过10w行的前端项目中使用,至少它在大型项目 的开发中的表现不错,它的存在是因为它帮我们解决了实际的问题呀。
: 再之,如果你真的想给某个领域解决一些问题,可你连现在这个领域都有些啥东西都不清楚,这不就是闭门造车嘛。
: ...................
--
FROM 110.85.22.*