From 2b53602d3c798b68db4ce986546d3434b986096a Mon Sep 17 00:00:00 2001 From: Benjamin Gruenbaum Date: Fri, 6 Aug 2021 20:21:29 +0300 Subject: [PATCH] feat: support AbortSignal in writeFile (#11568) --- cli/dts/lib.deno.ns.d.ts | 6 ++++++ cli/tests/unit/write_file_test.ts | 36 +++++++++++++++++++++++++++++++ runtime/js/40_write_file.js | 10 ++++++++- 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/cli/dts/lib.deno.ns.d.ts b/cli/dts/lib.deno.ns.d.ts index b0c56591bb..8aa478f12f 100644 --- a/cli/dts/lib.deno.ns.d.ts +++ b/cli/dts/lib.deno.ns.d.ts @@ -1714,6 +1714,12 @@ declare namespace Deno { create?: boolean; /** Permissions always applied to file. */ mode?: number; + /** + * An abort signal to allow cancellation of the file write operation. + * If the signal becomes aborted the writeFile operation will be stopped + * and the promise returned will be rejected with an AbortError. + */ + signal?: AbortSignal; } /** Synchronously write `data` to the given `path`, by default creating a new diff --git a/cli/tests/unit/write_file_test.ts b/cli/tests/unit/write_file_test.ts index 4c4cecf430..c4c4377080 100644 --- a/cli/tests/unit/write_file_test.ts +++ b/cli/tests/unit/write_file_test.ts @@ -239,3 +239,39 @@ unitTest( assertEquals("Hello", actual); }, ); + +unitTest( + { perms: { read: true, write: true } }, + async function writeFileAbortSignal(): Promise { + const ac = new AbortController(); + const enc = new TextEncoder(); + const data = enc.encode("Hello"); + const filename = Deno.makeTempDirSync() + "/test.txt"; + queueMicrotask(() => ac.abort()); + try { + await Deno.writeFile(filename, data, { signal: ac.signal }); + } catch (e) { + assertEquals(e.name, "AbortError"); + } + const stat = Deno.statSync(filename); + assertEquals(stat.size, 0); + }, +); + +unitTest( + { perms: { read: true, write: true } }, + async function writeFileAbortSignalPreAborted(): Promise { + const ac = new AbortController(); + ac.abort(); + const enc = new TextEncoder(); + const data = enc.encode("Hello"); + const filename = Deno.makeTempDirSync() + "/test.txt"; + try { + await Deno.writeFile(filename, data, { signal: ac.signal }); + } catch (e) { + assertEquals(e.name, "AbortError"); + } + const stat = Deno.statSync(filename); + assertEquals(stat.size, 0); + }, +); diff --git a/runtime/js/40_write_file.js b/runtime/js/40_write_file.js index def9f6fee4..6ced4e0669 100644 --- a/runtime/js/40_write_file.js +++ b/runtime/js/40_write_file.js @@ -13,6 +13,9 @@ data, options = {}, ) { + if (options?.signal?.aborted) { + throw new DOMException("The write operation was aborted.", "AbortError"); + } if (options.create !== undefined) { const create = !!options.create; if (!create) { @@ -68,12 +71,17 @@ await chmod(path, options.mode); } + const signal = options?.signal ?? null; let nwritten = 0; - while (nwritten < data.length) { + while (!signal?.aborted && nwritten < data.length) { nwritten += await file.write(TypedArrayPrototypeSubarray(data, nwritten)); } file.close(); + + if (signal?.aborted) { + throw new DOMException("The write operation was aborted.", "AbortError"); + } } function writeTextFileSync(