-
Notifications
You must be signed in to change notification settings - Fork 31.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fs: add support for async iterators to fs.writeFile
#38525
base: main
Are you sure you want to change the base?
fs: add support for async iterators to fs.writeFile
#38525
Conversation
675ffcd
to
9a563a1
Compare
9a563a1
to
c9bba3f
Compare
lib/fs.js
Outdated
if (writeErr) { | ||
if (isUserFd) { | ||
callback(writeErr); | ||
} else { | ||
fs.close(fd, (err) => { | ||
callback(aggregateTwoErrors(err, writeErr)); | ||
}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that in general it would make more sense to continue iterating only after a write has finished.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. As it is this will have issues with backpressure, risking flooding the destination with pending write requests. Also, I'm missing the bit about exiting the for await
if the fs.write()
errors on any single iteration.
lib/fs.js
Outdated
); | ||
} | ||
fs.close(fd, callback); | ||
})(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's no catch
handler on the promise being returned here.
lib/fs.js
Outdated
@@ -2051,7 +2053,29 @@ function writeAll(fd, isUserFd, buffer, offset, length, signal, callback) { | |||
} | |||
return; | |||
} | |||
// write(fd, buffer, offset, length, position, callback) | |||
if (isCustomIterable(buffer)) { | |||
(async () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be clearer and more maintainable to separate this out into a separate top-level function.
lib/fs.js
Outdated
} | ||
); | ||
} | ||
fs.close(fd, callback); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The close here is problematic because writes may still be pending from the loop.
test/parallel/test-fs-write-file.js
Outdated
@@ -95,3 +96,82 @@ fs.open(filename4, 'w+', common.mustSucceed((fd) => { | |||
|
|||
process.nextTick(() => controller.abort()); | |||
} | |||
|
|||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be better to separate the tests for the new functionality into a separate isolated test file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a good idea but the implementation needs work.
a7cc927
to
3313df8
Compare
3313df8
to
785d76d
Compare
lib/fs.js
Outdated
if (writeErr) { | ||
handleWriteAllErrorCallback(fd, isUserFd, writeErr, callback); | ||
} else { | ||
writeAll(fd, isUserFd, buffer, offset, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that there's a mistake here that also exists in the fs/promises
version, where the code assumes that the whole chunk has been written but it might have only been partially written.
ba733d5
to
ccdb3d1
Compare
ccdb3d1
to
6f21783
Compare
176573e
to
c0b87d6
Compare
@Linkgoron I added the following test based on this test, but I was not able to reproduce the mistake. 😓 (The current implementation will pass.)
|
It's quite difficult to reproduce, and I'm not sure in which cases |
@Linkgoron |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a lot going on here and I’m not sure I fully agree with all of it. I’m a little bit overloaded the next few days. I won’t block this but would appreciate if we could wait a few days before landing this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good work! I've left a few notes on the implementation.
writeAllCustomIterable( | ||
fd, isUserFd, buffer, offset, length, signal, encoding, callback) | ||
.catch((reason) => { | ||
handleWriteAllErrorCallback(fd, isUserFd, reason, callback); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would schedule this via process.nextTick
. If this throws synchronously for whatever reason, it will lead to an unhandled rejection.
} | ||
}); | ||
} | ||
|
||
async function writeAllCustomIterable( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do not use async
with a callback. Mixing those two can only lead to bugs. I would recommend having a promisified version of fs.write()
and just use async/await.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mcollina Thanks for your suggestion 👍 . I'm curious about the advantages of having a promisified version of fs.write()
and using async/await
. Could you elaborate on why this approach might be better than mixing async with a callback? I'm interested in understanding the potential benefits and how it could prevent bugs.
Fixes: #38075
Checklist