为什么异步的代码不是语义上被处理成同步的呢? #43

Open
jiyinyiyong opened this Issue Jan 9, 2013 · 9 comments

Comments

Projects
None yet
4 participants
Owner

jiyinyiyong commented Jan 9, 2013

之前对 Haskell 产生疑问的时候就想过这个问题,,,
我曾经猜 Haskell 有这样的机制, 当代码包含封装的异步计算时, 看作是同步
LiveScript 的例子很说明我的问题, 并且符号借鉴自 Haskell 的:

do
  data <-! $.get 'ajaxtest'
  $ \.result .html data
  processed <-! $.get 'ajaxprocess', data
  $ \.result .append processed

alert 'hi'

语法和同步类似, 没有太多嵌套, 而运行却是异步的,

$.get('ajaxtest', function(data){
  $('.result').html(data);
  $.get('ajaxprocess', data, function(processed){
    $('.result').append(processed);
  });
});
alert('hi');

Haskell 里的 Monad 意思大概也包含了的,
然后问题在于函数复合, 就像 Lisp 早先考虑的函数复合那样:
coffee-js/history-of-lisp@436cb3a#L0R12

我困惑的是: 为什么有异步的函数就不能拿来复合? 而非要写成回调
比方说我用伪代码, procedure 函数是异步的, 但有类似的 return:

def procedure:
  a = task-a() # 2s 
  b = task-b() # 3s 但和 task-a 几乎同时开始
  return (a + b)

def call:
  c = procedure()
  print c
  return "finised"

call()

return 等待异步函数 task-a task-b 完成, 然后再执行 return (a + b)

那么代码的意义就是, 等待需要的数据完成, 然后开始计算, 不管通过同步还是异步
代码的每一次计算都会消耗时间, 虽然长度上会完全不属于同个数量级
但我认为, "计算在所需数据完成时开始"的思路是一致的, 就能有连贯的语法
... 貌似这是数据流计算模型已经说过的:
http://jsjedu.hxu.edu.cn/dxjsjjc/kcnr/wlkj/03architecture/detail/3-3-1.htm

数据流计算模型的操作前提:数据是否齐备。例如计算:y=a+b, 数据齐备的前提就是知道 ab 的值,能一步得出y的值,而计算 y=(a+b)*(c+d), 需要两步,先分别计算 a+b 的值和 c+d 的值,等得到这两个值后,即数据齐备后,再将这两个值相乘来计算 y 的值。这就是数据流计算模型的过程。

于是我想, 这和函数复合的思路没有分别, 为什么通常语言里没有呢,
特别是 Node, 大量采用回调, 而且 Haskell 也有些看不懂,,
是在实现上有难度的么? 或者有其他原因?


加上 Dataflow 的 Wiki 上提到的关于执行过程的描述:

Operations consist of "black boxes" with inputs and outputs, all of which are always explicitly defined. They run as soon as all of their inputs become valid, as opposed to when the program encounters them. Whereas a traditional program essentially consists of a series of statements saying "do this, now do this", a dataflow program is more like a series of workers on an assembly line, who will do their assigned task as soon as the materials arrive. This is why dataflow languages are inherently parallel; the operations have no hidden state to keep track of, and the operations are all "ready" at the same time.

ypyf commented Jan 9, 2013

既然是异步,怎么会堵塞?你的例子(procedure)本身就是同步的。

Owner

jiyinyiyong commented Jan 9, 2013

@ypyf 我之前没标注明白. 我意思是 task-a task-b 是 HTTP 请求之类的异步操作,
两个操作不相干, 可以被同时执行, 执行完毕赋值给 a b, return 在两者赋值完成之后执行,
那么虽然是异步执行的, 可语法依然用同步的语法, 函数复合照常进行..
能做到的话岂不是很好?

ypyf commented Jan 9, 2013

但是你写出来的代码确实是同步的,因为如果task-_不返回,你的函数就会一直阻塞。但是如果你给task-_传递一个延续参数就不用等待返回结果了。

Owner

jiyinyiyong commented Jan 9, 2013

@ypyf 这个执行还是按延续函数的方式的方式执行没错, 我想表达的也是那个意思

def procedure:
  a = task-a() # 2s 
  b = task-b() # 3s 但和 task-a 几乎同时开始
  return (a + b)

这段函数体里边, task-a task-b 都异步执行的, return 也会等他们, 但期间别的代码也可以执行

不同在于我想用正常习惯的语法去表示那样的一个操作步骤,
就是说同时让函数符合能进行, 还有同时异步的操作还是异步的操作

jiaojing commented Jan 9, 2013

很早以前看haskell的一个slide,上面说:数据流并发是终极奥义,恩

Owner

jiyinyiyong commented Jan 9, 2013

@Emerge 他编译过 JS 代码的?...

Liutos commented Jan 9, 2013

不需要回调的不就是利用续延么?

@ghost

ghost commented Jan 9, 2013

原因是非纯函数式时,依赖性无法从代码流中分析出来:比如你的例子,编译器/解释器怎么知道 task-b() 不依赖于 task-a() ? 这个问题不能确定,这两个task就无法并发,必须顺序执行。纯函数式的价值就在于这里可以确定没有依赖,所以可以并发运行。

Owner

jiyinyiyong commented Jan 9, 2013

@Emerge 到点子上了... 就是说 Haskell 封装的纯函数, 是必然能并行的,
而 Haskell 用 Monad 隔离的副作用,, 也为了保证纯函数能在编译阶段被确定下来..
是这样了吧,,,
我下午想过怎么确定依赖性,.. 是不是为此任何可变状态都被杜绝了?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment