Looks like node:test side effect. In my tests, promises have almost the same speed.
My tests:
with node:test (node v21.4.0):
✔ reading file 10,000 times with callback parallel (401.3785ms)
✔ reading file 10,000 times with callback blocking (711.3979ms)
✔ reading file 10,000 times with async parallel (512.4789ms)
✔ reading file 10,000 times with async blocking (1080.5988ms)
✔ reading file 10,000 times with promise parallel (474.045ms)
✔ reading file 10,000 times with promises blocking (755.0493ms)
without node:test (node v21.4.0):
reading file 10,000 times with callback parallel: 402.4896ms
reading file 10,000 times with callback blocking: 755.3545ms
reading file 10,000 times with async parallel: 413.0469ms
reading file 10,000 times with async blocking: 825.5176ms
reading file 10,000 times with promise parallel: 408.7469ms
reading file 10,000 times with promises blocking: 775.5422ms
without node:test (bun 1.0.26):
reading file 10,000 times with callback parallel: 580.7146ms
reading file 10,000 times with callback blocking: 437.1434ms
reading file 10,000 times with async parallel: 589.4524ms
reading file 10,000 times with async blocking: 397.4406ms
reading file 10,000 times with promise parallel: 584.5042ms
reading file 10,000 times with promises blocking: 415.0018ms
My test code:
// const test = require('node:test');
const assert = require('node:assert');
const fs = require('node:fs');
const tests = [];
const test = async (name, callback) => {
tests.push([name, callback]);
};
setTimeout(async () => {
for await (let item of tests) {
const [name, callback] = item;
const startTime = performance.now();
let done;
const promise = new Promise((resolve) => {
done = () => {
resolve();
console.log(`${name}: ${(performance.now() - startTime).toFixed(4)}ms`);
};
});
const res = callback(null, done);
if (res instanceof Promise) {
await res;
done();
}
await promise;
}
}, 0);
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()
}
})
}
});
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)
});
test('reading file 10,000 times with async parallel', async (t) => {
let allFiles = []
for (let i = 0; i < 10000; i++) {
allFiles.push(fs.promises.readFile("./text.txt", { encoding: 'utf-8'}))
}
return await Promise.all(allFiles)
.then(allFiles => {
return allFiles.forEach((data) => {
assert.strictEqual(data, "Hello, world");
})
})
});
test('reading file 10,000 times with async blocking', async (t) => {
for (let i = 0; i < 10000; i++) {
let data = await fs.promises.readFile("./text.txt", { encoding: 'utf-8'})
assert.strictEqual(data, "Hello, world");
}
});
test('reading file 10,000 times with promise parallel', (t, done) => {
let allFiles = []
for (let i = 0; i < 10000; i++) {
allFiles.push(fs.promises.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()
})
});
let read2 = (i, callback) => {
let data = fs.promises.readFile("./text.txt", { encoding: 'utf-8'})
.then(data => {
assert.strictEqual(data, "Hello, world")
i += 1
if (i === 10000) {
return callback()
}
read(i, callback)
})
}
test('reading file 10,000 times with promises blocking', (t, done) => {
read2(0, done)
});