是的,我说过了! 并行运行回调时,回调速度比并行运行 async/await 快 。当我们运行顺序回调时,速度仅提高了 。 4.8 倍 1.9 倍 在我收到一些关于我的不可靠测试的有用和善意的评论后,我对这篇文章进行了一些修改。 😂🙏 谢谢至 和 感谢您花时间将基准引向正确的方向。我的第一个失误是我实际上并没有等待代码执行完成,这严重扭曲了结果。第二个是我正在比较并行运行时和顺序运行时,这使得基准测试毫无价值。 里卡多·洛佩斯 瑞恩·坡 这是第二轮,它解决了我最初的错误。之前我说过: 最初,我写道,如果我们回到回调,NodeJS 的速度会快 34.7 倍! 🤣 错了。 不像我之前的糟糕基准那么令人印象深刻(并查看上下文的评论),但仍然有相当大的差异。 那么我到底测试了什么? 我在读取文件时将回调与 Promise 和 async/await 进行了 10,000 次比较。也许这是一个愚蠢的测试,但我想知道,哪个 I/O 速度更快。 然后我终于将 Node.js 中的回调与 Go 进行了比较! 现在,猜猜谁赢了? 我不会刻薄的。 。戈朗! 太长了 越低越好。结果以 。 ms 现在,真正的基准测试者?在这件事上对我宽容一些。但请留下您的评论,让我成为一个更好的人。 大家都在说 Node.js 很慢! 这让我很烦恼。 因为慢意味着什么?与所有基准一样,我的基准是上下文相关的。 我开始阅读有关 ,只是为了开始理解 。 事件循环 怎么运行的 但我主要了解的是,Node.js 将 I/O 任务传递到位于 Node.js 主可执行线程外部的队列上。该队列运行于 。许多线程可能会处理这些 I/O 操作。这就是 Node.js 可以发挥作用的地方:处理 I/O。 纯C 然而,承诺是在主单个可执行线程中处理的。异步/等待,很好,承诺但现在添加了阻塞。 那么回调比承诺更快吗? 让我们来测试一下。 首先。我的 !与他人合作的补充 。请务必注意我们正在使用哪些资源。充足的内存和CPU。 机器 业力 MacBook Pro (14-inch, 2021) Chip Apple M1 Pro Memory 32 GB Cores 10 NodeJS v20.8.1 所以我们有一个 文件,其中包含 消息 。 text.txt 原始 Hello, world echo "Hello, world" > text.txt 我们将使用本机 Node.js 读取此文本文件,这意味着零节点模块依赖性,因为我们不想因为宇宙中最重的物体而拖慢速度。 回调 并行回调 首先,让我们从 回调开始。我感兴趣的是能够以多快的速度一次性读取同一个文件。什么比并行更快? 并行 // > file-callback-parallel.test.mjs import test from 'node:test'; import assert from 'node:assert'; import fs from "node:fs"; test('reading file 10,000 times with callback parallel', (t, done) => { let count = 0; for (let i = 0; i < 10000; i++) { fs.readFile("./text.txt", { encoding: 'utf-8'}, (err, data) => { assert.strictEqual(data, "Hello, world"); count++ if (count === 10000) { done() } }) } }); 顺序回调 其次,我们再次有回调,但是是顺序的(或者更确切地说是阻塞的)。我感兴趣的是顺序读取同一文件的速度。已经很久没有进行回调调用回调了,再次尝试很有趣。虽然,看起来并不漂亮。 // > file-callback-blocking.test.mjs import test from 'node:test'; import assert from 'node:assert'; import fs from "node:fs"; let read = (i, callback) => { fs.readFile("./text.txt", { encoding: 'utf-8'}, (err, data) => { assert.strictEqual(data, "Hello, world"); i += 1 if (i === 10000) { return callback() } read(i, callback) }) } test('reading file 10,000 times with callback blocking', (t, done) => { read(0, done) }); 异步/等待 然后我们有异步/等待。我最喜欢的使用 Nodejs 的方式。 并行异步/等待 这是我可以通过 async/await 获得的最大并行度。我将所有 操作加载到一个数组中,并使用 等待它们。 readFile Promise.all // > file-async-parallel.test.mjs import test from 'node:test'; import assert from 'node:assert'; import fs from "node:fs/promises"; test('reading file 10,000 times with async parallel', async (t) => { let allFiles = [] for (let i = 0; i < 10000; i++) { allFiles.push(fs.readFile("./text.txt", { encoding: 'utf-8'})) } return await Promise.all(allFiles) .then(allFiles => { return allFiles.forEach((data) => { assert.strictEqual(data, "Hello, world"); }) }) }); 顺序异步/等待 这是写起来最简单、最简洁的一篇。 // > file-async-blocking.test.mjs import test from 'node:test'; import assert from 'node:assert'; import fs from "node:fs/promises"; test('reading file 10,000 times with async blocking', async (t) => { for (let i = 0; i < 10000; i++) { let data = await fs.readFile("./text.txt", { encoding: 'utf-8'}) assert.strictEqual(data, "Hello, world"); } }); 承诺 最后,我们有没有 async/await 的 Promise。我早已停止使用它们,转而使用 ,但我对它们是否高性能感兴趣。 async/await 平行的承诺 // > file-promise-parallel.test.mjs import test from 'node:test'; import assert from 'node:assert'; import fs from "node:fs/promises"; test('reading file 10,000 times with promise parallel', (t, done) => { let allFiles = [] for (let i = 0; i < 10000; i++) { allFiles.push(fs.readFile("./text.txt", { encoding: 'utf-8'})) } Promise.all(allFiles) .then(allFiles => { for (let i = 0; i < 10000; i++) { assert.strictEqual(allFiles[i], "Hello, world"); } done() }) }); 连续的承诺。 同样,我们要等待所有 操作的执行。 readFile // > file-promise-blocking.test.mjs import test from 'node:test'; import assert from 'node:assert'; import fs from "node:fs/promises"; test('reading file 10,000 times with promises blocking', (t, done) => { let count = 0; for (let i = 0; i < 10000; i++) { let data = fs.readFile("./text.txt", { encoding: 'utf-8'}) .then(data => { assert.strictEqual(data, "Hello, world") count++ if (count === 10000) { done() } }) } }); 瞧!结果🎉!我什至运行了几次以获得更好的阅读效果。 我通过执行以下操作来运行每个测试: node --test <file>.mjs 它也比并行 Promise 快 4.7 倍! 使用回调读取文件 10,000 次比并行使用 async/await 快 5.8 倍以上! 因此,在 Node.js 领域,回调的性能 ! 更高 现在 Go 比 Node.js 更快吗? 好吧,我不是用 Go 编写的,所以这可能是真正糟糕的代码,因为我要求 ChatGPT 帮助我,但它 相当不错。 看起来 嘿嗬。我们走吧。我们的 Golang 代码。 package main import ( "fmt" "io/ioutil" "time" ) func main() { startTime := time.Now() for i := 0; i < 10000; i++ { data, err := ioutil.ReadFile("./text.txt") if err != nil { fmt.Printf("Error reading file: %v\n", err) return } if string(data) != "Hello, world" { fmt.Println("File content mismatch: got", string(data), ", want Hello, world") return } } duration := time.Since(startTime) fmt.Printf("Test execution time: %v\n", duration) } 我们这样运行它: go run main.go 结果如何? Test execution time: 58.877125ms 🤯 使用顺序回调,Go 比 Node.js 快 4.9 倍。 Node.js 只有通过并行执行才能接近。 Node.js Async/await 比 Go 慢 9.2 倍。 所以是的。 Node.js 速度较慢。尽管如此,在 300 毫秒内处理 10,000 个文件也不容小觑。但 Go 的速度让我感到谦卑! 现在只是一个旁注。我有不好的基准吗? 我确实有糟糕的基准。再次感谢里卡多和瑞安。 是的,我做到了。希望现在他们更好了。 但您可能会问,谁真的会一遍又一遍地读取同一个文件?但对于事物之间的相对测试,我希望这是一个有用的比较。 我也不知道 Node.js 使用了多少个线程。 我不知道我的 CPU 核心如何影响 Go 与 Node.js 的性能。 我可以租一台只有一个核心的 AWS 机器并进行比较。 是因为我用的是 Mac M1 吗? Node.js 在 Linux 或 Windows 上的执行情况如何? 😱 还有一个实用性,是的,读取文件是一回事,但在某些时候,您必须等待读取文件才能对文件中的数据执行某些操作。因此,主线程的速度仍然非常重要。 现在,您真的想使用回调吗? 我的意思是,你真的真的想要吗? 我不知道。我绝对不想告诉任何人该怎么做。 但我喜欢 async/await 简洁的语法。 他们看起来更好了。 他们读得更好。 我知道这里的更好是主观的,但我记得回调地狱,当承诺出现时我很感激。它让 Javascript 变得可以忍受。 现在,Golang 在最佳状态下明显比 Node.js 快,在回调和异步/等待方面,快了 9.2 倍!因此,如果我们想要良好的可读性和性能,Golang 是赢家。尽管如此,我很想了解 Golang 的底层是什么样的。 任何人。这很有趣。这更多的是帮助我理解回调和 I/O 在事件循环中如何工作的练习。 所以要退出 Node.js 慢吗?或者我们只是在慢速模式下使用 Node.js? 也许在性能很重要的地方,Golang 值得一跳。将来我肯定会更多地考虑使用 Golang。 也出现 。 在这里