回调与基于 promise 的操作的顺序
因为它们是由底层线程池异步地执行,所以当使用回调或基于 promise 的方法时无法保证顺序。
例如,以下内容容易出错,因为 fs.stat()
操作可能会在 fs.rename()
操作之前完成:
fs.rename('/tmp/hello', '/tmp/world', (err) => {
if (err) throw err;
console.log('renamed complete');
});
fs.stat('/tmp/world', (err, stats) => {
if (err) throw err;
console.log(`stats: ${JSON.stringify(stats)}`);
});
通过在调用另一个之前等待前一个的结果来正确地排序操作,这是很重要的:
import { rename, stat } from 'node:fs/promises';
const from = '/tmp/hello';
const to = '/tmp/world';
try {
await rename(from, to);
const stats = await stat(to);
console.log(`stats: ${JSON.stringify(stats)}`);
} catch (error) {
console.error('there was an error:', error.message);
}
const { rename, stat } = require('node:fs/promises');
(async function(from, to) {
try {
await rename(from, to);
const stats = await stat(to);
console.log(`stats: ${JSON.stringify(stats)}`);
} catch (error) {
console.error('there was an error:', error.message);
}
})('/tmp/hello', '/tmp/world');
或者,当使用回调 API 时,将 fs.stat()
调用移动到 fs.rename()
操作的回调中。
import { rename, stat } from 'node:fs';
rename('/tmp/hello', '/tmp/world', (err) => {
if (err) throw err;
stat('/tmp/world', (err, stats) => {
if (err) throw err;
console.log(`stats: ${JSON.stringify(stats)}`);
});
});
const { rename, stat } = require('node:fs/promises');
rename('/tmp/hello', '/tmp/world', (err) => {
if (err) throw err;
stat('/tmp/world', (err, stats) => {
if (err) throw err;
console.log(`stats: ${JSON.stringify(stats)}`);
});
});
Because they are executed asynchronously by the underlying thread pool, there is no guaranteed ordering when using either the callback or promise-based methods.
For example, the following is prone to error because the fs.stat()
operation might complete before the fs.rename()
operation:
fs.rename('/tmp/hello', '/tmp/world', (err) => {
if (err) throw err;
console.log('renamed complete');
});
fs.stat('/tmp/world', (err, stats) => {
if (err) throw err;
console.log(`stats: ${JSON.stringify(stats)}`);
});
It is important to correctly order the operations by awaiting the results of one before invoking the other:
import { rename, stat } from 'node:fs/promises';
const from = '/tmp/hello';
const to = '/tmp/world';
try {
await rename(from, to);
const stats = await stat(to);
console.log(`stats: ${JSON.stringify(stats)}`);
} catch (error) {
console.error('there was an error:', error.message);
}
const { rename, stat } = require('node:fs/promises');
(async function(from, to) {
try {
await rename(from, to);
const stats = await stat(to);
console.log(`stats: ${JSON.stringify(stats)}`);
} catch (error) {
console.error('there was an error:', error.message);
}
})('/tmp/hello', '/tmp/world');
Or, when using the callback APIs, move the fs.stat()
call into the callback
of the fs.rename()
operation:
import { rename, stat } from 'node:fs';
rename('/tmp/hello', '/tmp/world', (err) => {
if (err) throw err;
stat('/tmp/world', (err, stats) => {
if (err) throw err;
console.log(`stats: ${JSON.stringify(stats)}`);
});
});
const { rename, stat } = require('node:fs/promises');
rename('/tmp/hello', '/tmp/world', (err) => {
if (err) throw err;
stat('/tmp/world', (err, stats) => {
if (err) throw err;
console.log(`stats: ${JSON.stringify(stats)}`);
});
});