- 主题:花了一天实现了一个协程库
而且说到saga它有一个比较重要的设计理念是把副作用的描述和执行分开了,这个你用async 和await是没法做的
【 在 hgoldfish 的大作中提到: 】
:
: 我前面不说了吗?? generator 和 coroutine 的区别。
:
: 你就当做一个 async/await 版本的 saga 就行了。
:
: --
: 灭绝人性啊
:
:
发自「今日水木 on iPhone X」
--
FROM 114.85.182.*
协程也是 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.*
好的好的,首先你说那一堆优雅和吊死的写法不是我的看法,我两种都用。
我一直在尝试理解你这么做的实际应用场景是什么(优于现有方案的点在哪),所以问了很多问题。如果只是想给大家介绍下协程0,0,好吧……
【 在 hgoldfish 的大作中提到: 】
:
: 协程也是 js 的啊。总不能说用 promise/generator 才是真 js 程序员。不能说像我这种使用 coroutine 的是异端,要被吊死吧?
:
: \[\].forEach(...) // 优雅!
: for (let ... of ...) // 吊死!
:
: ..................
发自「今日水木 on iPhone X」
--
FROM 114.85.182.*
首先, 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.*
如果你理解saga的设计就不会这么说了,副作用的描述和执行分离是非常先进的理念。
【 在 hgoldfish 的大作中提到: 】
:
: 首先, generator 和 coroutine 都比 callback 更容易编写,这点你同意吧。至少 generator/corotuine 的写法能比 callback 少很多缩进等级。在我看来,js 社区现在流行两空格缩进简直是业界奇葩——callback 导致缩进太多是唯一的解
: ..................
发自「今日水木 on iPhone X」
--
FROM 114.85.182.*
反正我对那些框架没啥兴趣。
很好奇地问,js 不是函数式编程语言,没法从语法上面验证一个函数是纯的,没法规定一个对象是 immutable, 甚至连尾递归优化都没有。你们这些执着于这种虚妄的“先进”,何不考虑直接使用 ClosureScript 这些正规的函数式编程语言算了。
【 在 pangwa (学门手艺,混口饭吃.) 的大作中提到: 】
: 如果你理解saga的设计就不会这么说了,副作用的描述和执行分离是非常先进的理念。
: 发自「今日水木 on iPhone X」
--
FROM 110.85.22.*
一是js也没说的这么不堪,二也不是我一个人写代码啊。 搞工程不就是各种折衷么。 语言之争没啥意思,纯函数式语言也搞过,感觉还是过于阳春白雪了。
至于框架,saga我们在实际的工程中有过超过10w行的前端项目中使用,至少它在大型项目 的开发中的表现不错,它的存在是因为它帮我们解决了实际的问题呀。
再之,如果你真的想给某个领域解决一些问题,可你连现在这个领域都有些啥东西都不清楚,这不就是闭门造车嘛。
【 在 hgoldfish 的大作中提到: 】
:
: 反正我对那些框架没啥兴趣。
:
: 很好奇地问,js 不是函数式编程语言,没法从语法上面验证一个函数是纯的,没法规定一个对象是 immutable, 甚至连尾递归优化都没有。你们这些执着于这种虚妄的“先进”,何不考虑直接使用 ClosureScript 这些正规的函数式编程语言算了。
: ..................
发自「今日水木 on iPhone X」
--
FROM 114.85.182.*
这不是没找到么。。如果有人给我推荐一个好用的 js 协程库。我就不用浪费一个周末了。
话说,js 造协程库真是比其它语言简单多了。
【 在 pangwa (学门手艺,混口饭吃.) 的大作中提到: 】
: 一是js也没说的这么不堪,二也不是我一个人写代码啊。 搞工程不就是各种折衷么。 语言之争没啥意思,纯函数式语言也搞过,感觉还是过于阳春白雪了。
: 至于框架,saga我们在实际的工程中有过超过10w行的前端项目中使用,至少它在大型项目 的开发中的表现不错,它的存在是因为它帮我们解决了实际的问题呀。
: 再之,如果你真的想给某个领域解决一些问题,可你连现在这个领域都有些啥东西都不清楚,这不就是闭门造车嘛。
: ...................
--
FROM 110.85.22.*
下午手机码字很费劲, 这个loginEvent 的模拟, 用saga写应该大致类似这样:
function* app() {
yield fork(loginFlow)
yield fork(loadDataFlow)
}
function* loginFlow() {
const loginData = yield take('login-form-submitted')
const result = yield loginUsingData(loginData)
if (result.success) {
yield put('login-success', result.userInfo)
yield take('request-logout')
yield logout(result.userInfo.uid)
} else {
yield put('login-failed', result.reason)
}
}
function* loadDataFlow() {
while(true) {
const info = yield take('login-success')
yield loadUserInfo(info.uid)
yield take('logout')
}
}
【 在 hgoldfish (老鱼) 的大作中提到: 】
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 101.85.232.*
没错啊。take 和 put 就是 event.wait 和 event.set. 换个名字而已。。其实他们都是 promise 的另外一个写法。publish/subscribe 什么的,换汤不换药。我上面说了,Event 就是协程使用的 promise,只是协程环境下不建议直接使用 promise.
你看看用 saga 要怎么实现这个功能,刚好演示一下 Semaphore:
let smp = Semaphore(5);
let operations = CoroutineGroup();
let urls = [...]; // 一共十条链接
let responses = Coroutine.map(async (url) => {
await smp.acquire();
try {
let r = http.get(url);
return r.json();
} finally {
smp.release();
}
}, urls);
以上代码向后台请求十条 URL,但每次只发出五条请求,一条请求结束后才发出另外一条。
它的特化形式就是 Lock. 应用场景是,比如登录模块是受框架调用的,可能一次发出三条登录请求,显然这是不合理的,这时候就可以用 Lock. 用 Promise 实现也简单,设置一个变量一开始设为 null,第一个请求将它设置为 new Promise(),其它请求等待它的 resolved 就行了。都是相通的。
【 在 pangwa (学门手艺,混口饭吃.) 的大作中提到: 】
: 下午手机码字很费劲, 这个loginEvent 的模拟, 用saga写应该大致类似这样:
: function* app() {
: yield fork(loginFlow)
: ...................
--
FROM 112.47.122.*