同时使用多种编程语言写代码的痛


我觉得“痛”这个词也不恰当,但是我也知道该用哪个词来形容。我考虑过“困境”、“难点”、“不爽”、“苦楚”等等,都不太恰当。

算了,我还是来形容一下场景吧。

我是一名多语言使用者,编程时同时用 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

以上