Skip to content

Instantly share code, notes, and snippets.

@zenparsing
Last active August 29, 2015 14:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zenparsing/26b200543bb8ae0ca4df to your computer and use it in GitHub Desktop.
Save zenparsing/26b200543bb8ae0ca4df to your computer and use it in GitHub Desktop.
Reading and Writing Files with Async Generators
/*
This example uses an inner generator and skips over the first iteration in order to
prevent data loss which occurs on the first call to next.
*/
import * as FS from "async-fs";
export function readFile(path) {
async function* inner() {
let fd = await FS.open(path, "r"),
position = 0,
bytesRead,
buffer;
try {
// Send filled buffer view to consumer and wait until consumer has called
// `next` with a buffer to fill.
while (buffer = await yield buffer) {
// Stop if past EOF
if (bytesRead === 0)
break;
bytesRead = await FS.read(fd, buffer, buffer.length, position);
position += bytesRead;
buffer = buffer.slice(0, written);
}
} finally {
await FS.close(fd);
}
}
// Skip past the first iteration in order to prevent data loss
var iter = inner();
iter.next();
return iter;
}
export function writeFile(path) {
async function* inner() {
let fd = await FS.open(path, "w"),
position = 0,
buffer;
try {
// Wait until consumer has called `next` with a buffer to write
while (buffer = await yield null) {
let offset = position,
position += buffer.length;
// Write to file if buffer is not empty
if (position > offset)
await FS.write(fd, buffer, 0, buffer.length, offset);
}
} finally {
await FS.close(fd);
}
}
// Skip past the first iteration in order to prevent data loss
var iter = inner();
iter.next();
return iter;
}
/*
This example uses a decorator function which starts the generator and skips over the first
element in the sequence to prevent data loss.
*/
import * as FS from "async-fs";
export const readFile = skipFirst(async function*(path) {
let fd = await FS.open(path, "r"),
position = 0,
bytesRead,
buffer;
try {
// Send filled buffer view to consumer and wait until consumer has called
// `next` with a buffer to fill.
while (buffer = await yield buffer) {
// Stop if past EOF
if (bytesRead === 0)
break;
bytesRead = await FS.read(fd, buffer, buffer.length, position);
position += bytesRead;
buffer = buffer.slice(0, written);
}
} finally {
await FS.close(fd);
}
});
export const writeFile = skipFirst(async function*(path) {
let fd = await FS.open(path, "w"),
position = 0,
buffer;
try {
// Wait until consumer has called `next` with a buffer to write
while (buffer = await yield null) {
let offset = position,
position += buffer.length;
// Write to file if buffer is not empty
if (position > offset)
await FS.write(fd, buffer, 0, buffer.length, offset);
}
} finally {
await FS.close(fd);
}
});
/*
This example uses a hypothetical keyword `next` to access the argument supplied to the
`next` method of the generator object.
*/
import * as FS from "async-fs";
export async function* readFile(path) {
let fd = await FS.open(path, "r"),
position = 0;
try {
while (true) {
// Wait for a buffer from the consumer
let buffer = await next;
// Read chunk from file into buffer
let bytesRead = await FS.read(fd, buffer, buffer.length, position);
// Stop if past EOF
if (bytesRead === 0)
break;
position += bytesRead;
// Send filled buffer view to consumer
yield buffer.slice(0, written);
}
} finally {
await FS.close(fd);
}
}
export async function* writeFile(path) {
let fd = await FS.open(path, "w"),
position = 0;
try {
while (true) {
// Wait for a buffer from the consumer
let buffer = await next,
offset = position,
position += buffer.length;
// Write to file if buffer is not empty
if (position > offset)
await FS.write(fd, buffer, 0, buffer.length, offset);
yield null;
}
} finally {
await FS.close(fd);
}
}
/*
This example overrides the core generator methods and uses a closure variable to
provide access to the input argument.
*/
import * as FS from "async-fs";
function DataSink(init) {
let input = {},
gen = init(input);
// Intercept the core generator methods, capturing the input value
gen.prototype = new class extends gen.prototype.constructor {
next(val) { input.value = val; return super(val); }
throw(val) { input.value = val; return super(val); }
return(val) { input.value = val; return super(val); }
};
return gen;
}
export const readFile = DataSink(input => async function*(path) {
let fd = await FS.open(path, "r"),
position = 0;
try {
while (true) {
// Wait for a buffer from the consumer
let buffer = await input.value;
// Read chunk from file into buffer
let bytesRead = await FS.read(fd, buffer, buffer.length, position);
// Stop if past EOF
if (bytesRead === 0)
break;
position += bytesRead;
// Send filled buffer view to consumer
yield buffer.slice(0, written);
}
} finally {
await FS.close(fd);
}
});
export const writeFile = DataSink(input => async function*(path) {
let fd = await FS.open(path, "w"),
position = 0;
try {
while (true) {
// Wait for a buffer from the consumer
let buffer = await input.value,
offset = position,
position += buffer.length;
// Write to file if buffer is not empty
if (position > offset)
await FS.write(fd, buffer, 0, buffer.length, offset);
yield null;
}
} finally {
await FS.close(fd);
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment