我正在学习 javascript 和 nodejs,但仍然在异步执行方面苦苦挣扎
我知道下面的代码远非最佳实践,但我需要理解为什么,根据是否调用 fileHandle.write('[1]') ,当 fileHandle.write( '[2]') 被调用。
我在nodejs文档中找到了这个,我猜它是相关的,但我想了解事情是如何工作的: “在同一个文件上多次使用 filehandle.write() 而不等待承诺得到解决(或拒绝)是不安全的。对于这种情况,请使用 filehandle.createWriteStream()。”
没有错误的代码:
import {createServer} from 'node:http'
import {open} from 'node:fs/promises'
import {write} from 'node:fs'
import {json} from 'node:stream/consumers'
const serveur = createServer(async (request,response) => {
const path = './tp_CRUD/storage/my_file.json'
const fileHandle = await open(path, 'w+')
try {
await fileHandle.read()
.then(() => {
fileHandle.write('[1]') // <= no error as the file is still opened at this point
json(request) // promise parsing request content to json
.then(() => {
fileHandle.write('[2]') // <= no error as file is still open somehow thanks to [1] line
})
})
} catch {
console.log('error with POST request')
} finally {
fileHandle.close()
}
response.end()
})
serveur.listen('3000')
有错误的代码:
import {createServer} from 'node:http'
import {open} from 'node:fs/promises'
import {write} from 'node:fs'
import {json} from 'node:stream/consumers'
const serveur = createServer(async (request,response) => {
const path = './tp_CRUD/storage/my_file.json'
const fileHandle = await open(path, 'w+')
try {
await fileHandle.read()
.then(() => {
json(request) // promise parsing request content to json
.then(() => {
fileHandle.write('[2]') // <= error as file is closed despite being in the try{} block ??
})
})
} catch {
console.log('error with POST request')
} finally {
fileHandle.close()
}
response.end()
})
serveur.listen('3000')
这是调用堆栈,如果它能以某种方式提供帮助的话
node:internal/fs/promises:436
const err = new Error('file closed');
^
Error: file closed
at fsCall (node:internal/fs/promises:436:17)
at FileHandle.write (node:internal/fs/promises:207:12)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
code: 'EBADF',
syscall: 'write'
}
Node.js v21.1.0
调用的方式启动了两个异步链,这会导致竞争条件,因为不能保证写调用在关闭调用发生之前发生。
当你有
await fileHandle.read()
.then(() => {
// A
json(request)
.then(() => {
// B
fileHandle.write('[2]') // <= error as file is closed despite being in the try{} block ??
})
您正在排队一个与 A 无关的新异步任务 B,并且执行可能会在 B 运行之前继续。如果你想将它们链接在一起,你必须让
then()
函数返回一个 Promise 来附加到链中。
如果您想继续使用
then()
符号,您需要将它们链接在一起,如下所示:
await fileHandle.read()
.then(() => json(request) )
.then(() => fileHandle.write('[2]') )
这样,
await
调用将等待整个链。
或者,您可以完全丢失
then()
符号,而只使用 await
,如下所示:
await fileHandle.read()
await json(request)
await fileHandle.write('[2]')