他們終于承認(rèn):JavaScript 不適合做服務(wù)端
TypeScript 編譯器遷移至 Go 的決定,終于印證了一部分開發(fā)者多年來的吐槽。
前段時間,微軟宣布將 TypeScript 編譯器從 JavaScript 遷移到 Go,以實現(xiàn)高達(dá) 10 倍的性能提升——這一舉動,說白了就是承認(rèn)了一件我們早就知道的事:
JavaScript 并不適合處理服務(wù)端的高計算負(fù)載。
作為一個曾無數(shù)次調(diào)優(yōu) Node.js 服務(wù)卻最終撞上性能天花板的開發(fā)者,這感覺就像是——微軟在開發(fā)者大會現(xiàn)場親口承認(rèn):“我們努力讓 JavaScript 扛大旗,但物理規(guī)律還是擺在那里。”
那些關(guān)于服務(wù)端 JavaScript 的夢魘
幾年前,接到一個任務(wù),要構(gòu)建一個能處理 GB 級科學(xué)數(shù)據(jù)的后臺服務(wù)。
技術(shù)主管拍板說:“用 Node.js 吧,團(tuán)隊都熟。”
——這大概就是本項目的最后遺言。
起初代碼看起來還算簡單:
const processDataBatch = (dataBatch) => {
return dataBatch.map(item => {
return calculateComplexModel(item);
});
};
function calculateComplexModel(dataPoint) {
let result = 0;
for (let i = 0; i < 1000000; i++) {
result += Math.sin(dataPoint.value * i) * Math.cos(i / dataPoint.weight);
}
return result;
}
app.post('/process-data', async (req, res) => {
const results = processDataBatch(req.body.data);
res.json({ results });
});一旦上線到生產(chǎn)環(huán)境,Node.js 的事件循環(huán)就開始“抽搐”,響應(yīng)時間飆升,儀表盤亮成了一棵圣誕樹。
性能基準(zhǔn)不會說謊
Benchmarks Game 上對 Node.js 與 Go 的對比,尤其在字符串密集型任務(wù)中,Go 的表現(xiàn)是 Node.js 的 6 倍!
六倍。
圖片
TypeScript 編譯器之痛
TypeScript編譯器以前的編譯邏輯(也就是大家每天用的那套 tsc):
const ts = require("typescript");
function compileProject(projectPath) {
console.time('Compilation Time');
const configPath = ts.findConfigFile(projectPath, ts.sys.fileExists, "tsconfig.json");
const configFile = ts.readConfigFile(configPath, ts.sys.readFile);
const parsedConfig = ts.parseJsonConfigFileContent(
configFile.config,
ts.sys,
path.dirname(configPath)
);
const program = ts.createProgram(parsedConfig.fileNames, parsedConfig.options);
const emitResult = program.emit();
console.timeEnd('Compilation Time');
return emitResult;
}
compileProject('./my-slow-ts-app');在大型項目中,這段流程有時能卡你幾分鐘。TypeScript 團(tuán)隊最后的解決方案是:干脆放棄 JS,全部改用 Go 重寫。
為什么 JS 不適合干這活
JavaScript 并不是為 CPU 密集型任務(wù)而生。它是解釋型語言、運行在單線程環(huán)境中,即便 V8 再強,也繞不過以下限制:
- 單線程模型,缺乏并發(fā)處理能力
- 即時編譯機制造成啟動慢
- GC 停頓不可控
- 無原生類型支持,數(shù)值處理低效
對比下 JS 和 Go 的素數(shù)計算實現(xiàn),你就懂了:
JavaScript:
function calculatePrimes(max) {
const sieve = new Array(max).fill(true);
sieve[0] = sieve[1] = false;
for (let i = 2; i <= Math.sqrt(max); i++) {
if (sieve[i]) {
for (let j = i * i; j < max; j += i) {
sieve[j] = false;
}
}
}
return sieve.reduce((count, isPrime) => isPrime ? count + 1 : count, 0);
}
console.time('JS Primes');
console.log(calculatePrimes(10000000));
console.timeEnd('JS Primes');Go:
func calculatePrimes(max int) int {
sieve := make([]bool, max)
for i := 2; i < max; i++ {
sieve[i] = true
}
for i := 2; i*i < max; i++ {
if sieve[i] {
for j := i * i; j < max; j += i {
sieve[j] = false
}
}
}
count := 0
for i := 2; i < max; i++ {
if sieve[i] {
count++
}
}
return count
}結(jié)果?Go 一半時間搞定。同樣的算法,效率差距不是細(xì)節(jié)優(yōu)化能彌補的。
JS 依然有它的舞臺
話雖如此,Node.js 在處理 I/O 密集型任務(wù) 上依然非常強大:
- 高并發(fā) HTTP 服務(wù)
- API 網(wǎng)關(guān)轉(zhuǎn)發(fā)與聚合
- WebSocket 實時通信
這些不是 JS 的弱項,反而是它最擅長的領(lǐng)域。
總結(jié)
微軟這次把 TypeScript 編譯器遷到 Go,是一次“企業(yè)級的認(rèn)錯”。說白了就是:
“我們當(dāng)初選錯了工具,現(xiàn)在換個對的。”
這不丟人。真正可怕的是明知不合適還死撐。



















