我觉得“痛”这个词也不恰当,但是我也知道该用哪个词来形容。我考虑过“困境”、“难点”、“不爽”、“苦楚”等等,都不太恰当。
算了,我还是来形容一下场景吧。
我是一名多语言使用者,编程时同时用 JavaScript 和 Elixir,日常生活中、日、英混杂。
因为使用 JavaScript 的频率太高了,有时我会把 JavaScript 的某些特性误以为是编程语言的共同特性,例如:await 。
相信,所有合格的 JavaScript 开发者都知道下面这段代码的输出:
async function foo(name) {
console.log(name, "start");
await console.log(name, "middle");
console.log(name, "end");
}
foo("First");
foo("Second");
掌握 JavaScript 中 await 的执行顺序,是作为一名合格的 JavaScript 开发者的起点。但是如果你不小心把 JavaScript await 的执行顺序错误理解成所有编程语言的共性,那你很容易就会在 Elixir 开发中变成一名“不合格”的开发者。
因为 Elixir 中异步是真实的,JavaScript 中异步是模拟的(也可以说是基于事件循环的)。
请大家猜一下,下面 Elixir 代码的输入
defmodule Demo do
def foo(name) do
IO.puts("#{name} start")
Process.sleep(500)
IO.puts("#{name} middle")
Process.sleep(500)
IO.puts("#{name} end")
end
end
Task.async(fn -> Demo.foo("First") end) |> Task.await
Task.async(fn -> Demo.foo("Second") end) |> Task.await
代码在评论区
为什么呢?
主要区别
- JavaScript:
- 单线程,使用事件循环来处理异步操作。
- async/await 本质上是语法糖,底层仍是基于 Promise + 回调,依赖事件循环切换任务。
- 在同一个事件循环中,只有一个线程在执行 JS 代码,I/O 等操作异步回调回来后再执行。
- Elixir(BEAM 虚拟机):
- 天生支持并发,每个进程都非常轻量(与操作系统进程不同),可以同时在多核 CPU 上运行。
- 使用 Task.async/await、spawn 等方式来创建并发任务,内部有自己的调度器,能把这些任务分配到不同的调度线程上并行执行。
- 因为是真正并发,输出可能会交错出现,具体顺序并不保证。
所以,虽然 JavaScript 和 Elixir 中都有 async await 这两个关键字,但是它们的动作和底层机制都是不一样的。
作为一名多语言使用者,我常常会因为熟练使用某一种语言,熟练到习以为常,而忘记其他语言的正确使用方法。编程语言如此,自然语言亦是如此。
在日本生活久了的人,无论曾经英语有多少,几乎都会忘记“电梯”的英文发音,😂
推荐一个介绍 JapanEnglish 的视频,https://youtu.be/q7y4av-Dr4I?si=DdJwLJapaif4CajG&t=40
以上