这个终端日志是 Node JS 异步性质的结果吗?
Is this terminal-log the consequence of the Node JS asynchronous nature?
我还没有发现任何关于此的具体信息,这不是真正的问题,但我想更好地了解这里发生了什么。
基本上,我正在测试一些简单的 NodeJS 代码,如下所示:
//Summary : Open a file , write to the file, delete the file.
let fs = require('fs');
fs.open('mynewfile.txt' , 'w' , function(err,file){
if(err) throw err;
console.log('Created file!')
})
fs.appendFile('mynewfile.txt' , 'Depois de ter criado este ficheiro com o fs.open, acrescentei-lhe data com o fs.appendFile' , function(err){
if (err) throw err;
console.log('Added text to the file.')
})
fs.unlink('mynewfile.txt', function(err){
if (err) throw err;
console.log('File deleted!')
})
console.log(__dirname);
我认为这段代码会按照从上到下编写的顺序执行,但是当我查看终端时我不确定是不是这样,因为这是我得到的:
$ node FileSystem.js
C:\Users\Simon\OneDrive\Desktop\Portfolio\Learning Projects\NodeJS_Tutorial
Created file!
File deleted!
Added text to the file.
//Expected order would be: Create file, add text to file , delete file , log dirname.
最终当我查看我的文件夹时,代码顺序似乎仍然以某种方式被遵循,而不是终端可能让您想到的,因为文件已被删除并且目录中没有任何内容。
所以,我想知道,为什么终端登录的顺序不是按照代码从上到下编写的顺序。
这是 NodeJS 异步性质的结果还是其他原因?
它是两件事的组合:
- Node.js 的异步性质,正如您正确假设的那样
- 能够取消链接打开的文件
可能发生的事情是这样的:
- 同时打开和创建了文件(
open
with flag w
)
- 文件被第二次打开以追加(
fs.appendFile
)
- 文件已取消链接
- 数据已附加到文件(虽然它已取消链接)并且文件已关闭
追加数据时,文件仍作为 inode 存在于磁盘上,但指向它的硬链接(引用)为零。它仍然占用 space,但是 OS 在关闭时检查引用计数,如果计数降为零则释放 space。
人们有时 运行 遇到与使用日志轮换的 HTTP 服务器等守护进程类似的情况:如果在切换日志时出现问题,旧日志文件可能会断开链接但不会关闭,因此永远不会清理完毕,它需要 space 永远(直到您重新启动或重新启动该过程)。
请注意,您观察到的操作顺序是随机的,并且它们可能会重新排序。不要依赖它。
如您所说,代码(原则上)是从上到下执行的。但是 fs.open
、fs.appendFile
和 fs.unlink
是异步的。即,它们以特定顺序放置在执行堆栈中,但无法保证它们以何种顺序完成,因此您无法保证回调的执行顺序。如果您多次 运行 代码,很有可能会遇到不同的执行顺序 ...
如果您需要特定的订单,您有两种不同的选择
后面的操作只能在前面的回调中调用,如下所示
fs.open('mynewfile.txt' , 'w' , function(err,file){
if(err) throw err;
console.log('Created file!')
fs.appendFile('mynewfile.txt' , '...' , function(err){
if (err) throw err;
console.log('Added text to the file.')
fs.unlink('mynewfile.txt', function(err){
if (err) throw err;
console.log('File deleted!')
})
})
})
你看,随着嵌套的增加,代码变得非常丑陋且难以阅读...
您切换到基于承诺的方法
let fs = require('fs').promises;
fs.open("myfile.txt", "w")
.then(file=> {
return fs.appendFile("myfile.txt", "...");
})
.then(res => {
return fs.unlink("myfile");
})
.catch(e => {
console.log(e);
})
配合promise版本的操作,也可以使用async/await
async function doit() {
let file = await fs.open('myfile.txt', 'w');
await fs.appendFile('myfile.txt', '...');
await fs.unlink('myfile.txt', '...');
}
对于所有三种可能性,您可能需要先关闭文件,然后才能取消链接。
有关更多详细信息,请阅读 Javascript
中的 Promises、async/await
和执行堆栈
你可以这样写(未经测试):
let fs = require('fs');
const main = async () => {
await fs.open('mynewfile.txt' , 'w');
await fs.appendFile('mynewfile.txt' , 'content');
await fs.unlink('mynewfile.txt');
});
main()
.then(() => console.log('success'()
.catch(console.error);
或在另一个异步函数中:
const someOtherFn = async () => {
try{
await main();
} catch(e) {
// handle any rejection to your liking
}
}
(catch 块不是强制性的。您可以选择让它们抛到顶部。这只是为了展示 async / await 如何让您使同步代码看起来就像是同步代码,而不会遇到回调地狱。)
我还没有发现任何关于此的具体信息,这不是真正的问题,但我想更好地了解这里发生了什么。
基本上,我正在测试一些简单的 NodeJS 代码,如下所示:
//Summary : Open a file , write to the file, delete the file.
let fs = require('fs');
fs.open('mynewfile.txt' , 'w' , function(err,file){
if(err) throw err;
console.log('Created file!')
})
fs.appendFile('mynewfile.txt' , 'Depois de ter criado este ficheiro com o fs.open, acrescentei-lhe data com o fs.appendFile' , function(err){
if (err) throw err;
console.log('Added text to the file.')
})
fs.unlink('mynewfile.txt', function(err){
if (err) throw err;
console.log('File deleted!')
})
console.log(__dirname);
我认为这段代码会按照从上到下编写的顺序执行,但是当我查看终端时我不确定是不是这样,因为这是我得到的:
$ node FileSystem.js
C:\Users\Simon\OneDrive\Desktop\Portfolio\Learning Projects\NodeJS_Tutorial
Created file!
File deleted!
Added text to the file.
//Expected order would be: Create file, add text to file , delete file , log dirname.
最终当我查看我的文件夹时,代码顺序似乎仍然以某种方式被遵循,而不是终端可能让您想到的,因为文件已被删除并且目录中没有任何内容。
所以,我想知道,为什么终端登录的顺序不是按照代码从上到下编写的顺序。 这是 NodeJS 异步性质的结果还是其他原因?
它是两件事的组合:
- Node.js 的异步性质,正如您正确假设的那样
- 能够取消链接打开的文件
可能发生的事情是这样的:
- 同时打开和创建了文件(
open
with flagw
) - 文件被第二次打开以追加(
fs.appendFile
) - 文件已取消链接
- 数据已附加到文件(虽然它已取消链接)并且文件已关闭
追加数据时,文件仍作为 inode 存在于磁盘上,但指向它的硬链接(引用)为零。它仍然占用 space,但是 OS 在关闭时检查引用计数,如果计数降为零则释放 space。
人们有时 运行 遇到与使用日志轮换的 HTTP 服务器等守护进程类似的情况:如果在切换日志时出现问题,旧日志文件可能会断开链接但不会关闭,因此永远不会清理完毕,它需要 space 永远(直到您重新启动或重新启动该过程)。
请注意,您观察到的操作顺序是随机的,并且它们可能会重新排序。不要依赖它。
如您所说,代码(原则上)是从上到下执行的。但是 fs.open
、fs.appendFile
和 fs.unlink
是异步的。即,它们以特定顺序放置在执行堆栈中,但无法保证它们以何种顺序完成,因此您无法保证回调的执行顺序。如果您多次 运行 代码,很有可能会遇到不同的执行顺序 ...
如果您需要特定的订单,您有两种不同的选择
后面的操作只能在前面的回调中调用,如下所示
fs.open('mynewfile.txt' , 'w' , function(err,file){ if(err) throw err; console.log('Created file!') fs.appendFile('mynewfile.txt' , '...' , function(err){ if (err) throw err; console.log('Added text to the file.') fs.unlink('mynewfile.txt', function(err){ if (err) throw err; console.log('File deleted!') }) }) })
你看,随着嵌套的增加,代码变得非常丑陋且难以阅读...
您切换到基于承诺的方法
let fs = require('fs').promises; fs.open("myfile.txt", "w") .then(file=> { return fs.appendFile("myfile.txt", "..."); }) .then(res => { return fs.unlink("myfile"); }) .catch(e => { console.log(e); })
配合promise版本的操作,也可以使用
async/await
async function doit() { let file = await fs.open('myfile.txt', 'w'); await fs.appendFile('myfile.txt', '...'); await fs.unlink('myfile.txt', '...'); }
对于所有三种可能性,您可能需要先关闭文件,然后才能取消链接。
有关更多详细信息,请阅读 Javascript
中的 Promises、async/await
和执行堆栈
你可以这样写(未经测试):
let fs = require('fs');
const main = async () => {
await fs.open('mynewfile.txt' , 'w');
await fs.appendFile('mynewfile.txt' , 'content');
await fs.unlink('mynewfile.txt');
});
main()
.then(() => console.log('success'()
.catch(console.error);
或在另一个异步函数中:
const someOtherFn = async () => {
try{
await main();
} catch(e) {
// handle any rejection to your liking
}
}
(catch 块不是强制性的。您可以选择让它们抛到顶部。这只是为了展示 async / await 如何让您使同步代码看起来就像是同步代码,而不会遇到回调地狱。)