Spec-Zone.ru › Deno 2

Глобальное пространство имён Deno содержит API, не входящие в веб-стандарт, в том числе API для чтения файлов, открытия TCP-сокетов, предоставления HTTP-сервисов и выполнения дочерних процессов и т. д.

Документация API

В этом разделе представлена подробная документация по всем API, специфичным для Deno и доступным через глобальное пространство имён Deno. Вы можете просмотреть все символы, чтобы увидеть полный список доступных API, или выполнить поиск по категориям. Нажмите любую функцию или интерфейс, чтобы просмотреть подробную документацию с примерами

Ниже мы расскажем о некоторых наиболее важных API Deno.

Файловая система

Среда выполнения Deno включает различные функции для работы с файлами и каталогами. Чтобы получить доступ к файловой системе, необходимо использовать разрешения --allow-read и --allow-write.

Примеры кода с использованием функций файловой системы приведены по ссылкам ниже.

  • Чтение файлов в потоковом режиме
  • Чтение текстового файла (Deno.readTextFile)
  • Запись текстового файла (Deno.writeTextFile)

Сеть

Среда выполнения Deno включает встроенные функции для работы с подключениями к сетевым портам.

Примеры кода для распространённых функций приведены по ссылкам ниже.

  • Подключение к имени хоста и порту (Deno.connect)
  • Прослушивание локального транспортного адреса (Deno.listen)

Практические примеры работы с сетевыми функциями:

  • HTTP-сервер: Hello world
  • HTTP-сервер: маршрутизация
  • TCP-сервер эхо
  • Пример использования WebSocket
  • Руководство по созданию чата с помощью WebSocket

Дочерние процессы

Среда выполнения Deno включает встроенные функции для запуска дочерних процессов.

Примеры кода для создания дочернего процесса приведены по ссылкам ниже.

  • Создание дочернего процесса (Deno.Command)
  • Сбор данных, выведенных дочерними процессами

Ошибки

Среда выполнения Deno включает 20 классов ошибок, которые могут возникнуть при различных условиях.

Например:

›_
Deno.errors.NotFound;
Deno.errors.WriteZero;

Их можно использовать следующим образом:

try {
  const file = await Deno.open("./some/file.txt");
} catch (error) {
  if (error instanceof Deno.errors.NotFound) {
    console.error("the file was not found");
  } else {
    // otherwise re-throw
    throw error;
  }
}

HTTP-сервер

В Deno есть два API HTTP-сервера:

  • Deno.serve: нативный API высокого уровня, поддерживающий HTTP/1.1 и HTTP2; рекомендуется для создания HTTP-серверов в Deno.
  • Deno.serveHttp: нативный API низкого уровня, поддерживающий HTTP/1.1 и HTTP2.

Чтобы запустить HTTP-сервер на заданном порту, используйте функцию Deno.serve. Эта функция принимает функцию-обработчик, вызываемую для каждого входящего запроса, и должна возвращать ответ (или промис, разрешающийся в ответ). Например:

Deno.serve((_req) => {
  return new Response("Hello, World!");
});

По умолчанию Deno.serve прослушивает порт 8000, но его можно изменить, передав номер порта в объекте параметров первым или вторым аргументом.

Подробнее о том, как использовать API HTTP-сервера.

Практические примеры HTTP-серверов:

  • Руководство по созданию простого файлового сервера
  • HTTP-сервер для раздачи файлов
  • HTTP-сервер с потоковой передачей данных
  • HTTP-сервер с WebSocket

Разрешения

Разрешения задаются в CLI при запуске команды deno. Пользовательский код часто предполагает наличие определённого набора необходимых разрешений, однако во время выполнения нет гарантии, что набор предоставленных разрешений будет соответствовать этим ожиданиям.

В некоторых случаях для создания отказоустойчивой программы требуется возможность взаимодействовать с системой разрешений во время выполнения.

Дескрипторы разрешений

В CLI разрешение на чтение для /foo/bar обозначается как --allow-read=/foo/bar. В среде выполнения JS оно представляется следующим образом:

const desc = { name: "read", path: "/foo/bar" } as const;

Другие примеры:

// Global write permission.
const desc1 = { name: "write" } as const;

// Write permission to `$PWD/foo/bar`.
const desc2 = { name: "write", path: "foo/bar" } as const;

// Global net permission.
const desc3 = { name: "net" } as const;

// Net permission to 127.0.0.1:8000.
const desc4 = { name: "net", host: "127.0.0.1:8000" } as const;

// High-resolution time permission.
const desc5 = { name: "hrtime" } as const;

Дополнительные сведения см. в разделе PermissionDescriptor справочника API. Для всех описанных ниже API существуют синхронные аналоги (например, Deno.permissions.querySync).

Проверка разрешений

Проверка того, предоставлено ли разрешение, по дескриптору.

// deno run --allow-read=/foo main.ts

const desc1 = { name: "read", path: "/foo" } as const;
console.log(await Deno.permissions.query(desc1));
// PermissionStatus { state: "granted", partial: false }

const desc2 = { name: "read", path: "/foo/bar" } as const;
console.log(await Deno.permissions.query(desc2));
// PermissionStatus { state: "granted", partial: false }

const desc3 = { name: "read", path: "/bar" } as const;
console.log(await Deno.permissions.query(desc3));
// PermissionStatus { state: "prompt", partial: false }

Если для ограничения некоторых путей к файлам использовался флаг --deny-read, результат будет содержать partial: true, указывающее, что разрешения предоставлены не для всех вложенных путей:

// deno run --allow-read=/foo --deny-read=/foo/bar main.ts

const desc1 = { name: "read", path: "/foo" } as const;
console.log(await Deno.permissions.query(desc1));
// PermissionStatus { state: "granted", partial: true }

const desc2 = { name: "read", path: "/foo/bar" } as const;
console.log(await Deno.permissions.query(desc2));
// PermissionStatus { state: "denied", partial: false }

const desc3 = { name: "read", path: "/bar" } as const;
console.log(await Deno.permissions.query(desc3));
// PermissionStatus { state: "prompt", partial: false }

Состояния разрешений

Разрешение может иметь состояние "granted", "prompt" или "denied". Для разрешений, предоставленных через CLI, запрос вернёт { state: "granted" }. Для разрешений, которые не были предоставлены, по умолчанию возвращается { state: "prompt" }, а { state: "denied" } используется для явно отклонённых разрешений. Это будет важно при запросе разрешений.

Степень разрешения

Интуитивно результат второго запроса в разделе Проверка разрешений означает, что разрешение на чтение предоставлено для /foo, а /foo/bar находится внутри /foo, поэтому чтение /foo/bar разрешено. Это справедливо, если только разрешение, предоставленное через CLI, не является частичным по отношению к запрошенным разрешениям (из-за использования флага --deny-*).

Можно также сказать, что desc1 сильнее, чем desc2. Это означает, что для любого набора разрешений, предоставленных через CLI:

  1. Если запрос desc1 возвращает { state: "granted", partial: false }, то и запрос desc2 должен возвращать то же самое.
  2. Если запрос desc2 возвращает { state: "denied", partial: false }, то и запрос desc1 должен возвращать то же самое.

Другие примеры:

const desc1 = { name: "write" } as const;
// is stronger than
const desc2 = { name: "write", path: "/foo" } as const;

const desc3 = { name: "net", host: "127.0.0.1" } as const;
// is stronger than
const desc4 = { name: "net", host: "127.0.0.1:8000" } as const;

Запрос разрешений

Запрос не предоставленного ранее разрешения у пользователя через приглашение в CLI.

// deno run main.ts

const desc1 = { name: "read", path: "/foo" } as const;
const status1 = await Deno.permissions.request(desc1);
// ⚠️ Deno requests read access to "/foo". Grant? [y/n (y = yes allow, n = no deny)] y
console.log(status1);
// PermissionStatus { state: "granted", partial: false }

const desc2 = { name: "read", path: "/bar" } as const;
const status2 = await Deno.permissions.request(desc2);
// ⚠️ Deno requests read access to "/bar". Grant? [y/n (y = yes allow, n = no deny)] n
console.log(status2);
// PermissionStatus { state: "denied", partial: false }

Если текущее состояние разрешения — "prompt", в терминале пользователя появится запрос на подтверждение предоставления разрешения. Запрос для desc1 был одобрен, поэтому возвращается его новое состояние, а выполнение продолжается так, как если бы в CLI был указан параметр --allow-read=/foo. Запрос для desc2 был отклонён, поэтому состояние разрешения меняется с "prompt" на "denied".

Если текущее состояние разрешения уже равно "granted" или "denied", запрос работает как проверка и просто возвращает текущее состояние. Это предотвращает повторные запросы для уже предоставленных разрешений и ранее отклонённых запросов.

Отзыв разрешений

Понижение состояния разрешения с "granted" до "prompt".

// deno run --allow-read=/foo main.ts

const desc = { name: "read", path: "/foo" } as const;
console.log(await Deno.permissions.revoke(desc));
// PermissionStatus { state: "prompt", partial: false }

Что происходит при попытке отозвать разрешение, которое является частичным по отношению к разрешению, предоставленному через CLI?

// deno run --allow-read=/foo main.ts

const desc = { name: "read", path: "/foo/bar" } as const;
console.log(await Deno.permissions.revoke(desc));
// PermissionStatus { state: "prompt", partial: false }
const cliDesc = { name: "read", path: "/foo" } as const;
console.log(await Deno.permissions.revoke(cliDesc));
// PermissionStatus { state: "prompt", partial: false }

Разрешение, предоставленное через CLI и включающее отозванное разрешение, также было отозвано.

Чтобы понять это поведение, представьте, что Deno хранит внутренний набор явно предоставленных дескрипторов разрешений. Указание --allow-read=/foo,/bar в CLI инициализирует этот набор следующим образом:

[
  { name: "read", path: "/foo" },
  { name: "read", path: "/bar" },
];

Предоставление запрошенного во время выполнения разрешения { name: "write", path: "/foo" } обновляет набор следующим образом:

[
  { name: "read", path: "/foo" },
  { name: "read", path: "/bar" },
  { name: "write", path: "/foo" },
];

Алгоритм отзыва разрешений в Deno удаляет из этого набора каждый элемент, который сильнее дескриптора разрешения, переданного в качестве аргумента.

Deno не допускает «фрагментированных» состояний разрешений, при которых предоставлено сильное разрешение, но исключены подразумеваемые им слабые разрешения. Такая система становилась бы всё более сложной и непредсказуемой с учётом растущего числа сценариев использования и состояния "denied". Это осознанный компромисс в пользу безопасности за счёт детализации.

import.meta

Deno поддерживает ряд свойств и методов API import.meta. С его помощью можно получать сведения о модуле, например URL модуля.

import.meta.url

Возвращает URL текущего модуля.

main.ts
console.log(import.meta.url);
›_
$ deno run main.ts
file:///dev/main.ts

$ deno run https:/example.com/main.ts
https://example.com/main.ts

import.meta.main

Возвращает значение, указывающее, является ли текущий модуль точкой входа программы.

main.ts
import "./other.ts";

console.log(`Is ${import.meta.url} the main module?`, import.meta.main);
other.ts
console.log(`Is ${import.meta.url} the main module?`, import.meta.main);
›_
$ deno run main.ts
Is file:///dev/other.ts the main module? false
Is file:///dev/main.ts the main module? true

import.meta.filename

Это свойство доступно только для локальных модулей (модулей с указателем file:///...) и возвращает undefined для удалённых модулей.

Возвращает полный путь к текущему модулю. Значение содержит разделители путей, специфичные для операционной системы.

main.ts
console.log(import.meta.filename);

В Unix:

›_
$ deno run main.ts
/dev/main.ts

$ deno run https://example.com/main.ts
undefined

В Windows:

›_
$ deno run main.ts
C:\dev\main.ts

$ deno run https://example.com/main.ts
undefined

import.meta.dirname

Это свойство доступно только для локальных модулей (модулей с указателем file:///...) и возвращает undefined для удалённых модулей.

Возвращает полный путь к каталогу, содержащему текущий модуль. Значение содержит разделители путей, специфичные для операционной системы.

main.ts
console.log(import.meta.dirname);

В Unix:

›_
$ deno run main.ts
/dev/

$ deno run https://example.com/main.ts
undefined

В Windows:

›_
$ deno run main.ts
C:\dev\

$ deno run https://example.com/main.ts
undefined

import.meta.resolve

Разрешает спецификаторы относительно текущего модуля.

const worker = new Worker(import.meta.resolve("./worker.ts"));

API import.meta.resolve учитывает действующую карту импортов, благодаря чему можно разрешать и «голые» спецификаторы.

После загрузки такой карты импортов...

{
  "imports": {
    "fresh": "https://deno.land/x/fresh@1.0.1/dev.ts"
  }
}

...теперь можно разрешать:

resolve.js
console.log(import.meta.resolve("fresh"));
›_
$ deno run resolve.js
https://deno.land/x/fresh@1.0.1/dev.ts

FFI

API FFI (интерфейс для вызова внешних функций) позволяет вызывать с помощью Deno.dlopen библиотеки, написанные на языках программирования, поддерживающих ABI языка C (C/C++, Rust, Zig, V и т. д.).

Пример вызова функции Rust из Deno:

// add.rs
#[unsafe(no_mangle)]
pub extern "C" fn add(a: isize, b: isize) -> isize {
    a + b
}

Скомпилируйте её в динамическую библиотеку C (libadd.so в Linux):

›_
rustc --crate-type cdylib add.rs

На C это можно записать так:

// add.c
int add(int a, int b) {
  return a + b;
}

Затем скомпилируйте её:

›_
// unix
cc -c -o add.o add.c
cc -shared -W -o libadd.so add.o
// Windows
cl /LD add.c /link /EXPORT:add

Вызов библиотеки из Deno:

// ffi.ts

// Determine library extension based on
// your OS.
let libSuffix = "";
switch (Deno.build.os) {
  case "windows":
    libSuffix = "dll";
    break;
  case "darwin":
    libSuffix = "dylib";
    break;
  default:
    libSuffix = "so";
    break;
}

const libName = `./libadd.${libSuffix}`;
// Open library and define exported symbols
const dylib = Deno.dlopen(
  libName,
  {
    "add": { parameters: ["isize", "isize"], result: "isize" },
  } as const,
);

// Call the symbol `add`
const result = dylib.symbols.add(35, 34); // 69

console.log(`Result from external addition of 35 and 34: ${result}`);

Запустите с флагами --allow-ffi и --unstable:

›_
deno run --allow-ffi ffi.ts

Неблокирующий FFI

Во многих случаях может понадобиться выполнять ресурсоёмкие, ограниченные производительностью процессора функции FFI в фоновом режиме, не блокируя другие задачи в основном потоке.

Начиная с Deno 1.15 символы в Deno.dlopen можно помечать как nonblocking. Такие вызовы функций выполняются в отдельном блокирующем потоке и возвращают Promise, разрешающийся в нужный result.

Пример выполнения ресурсоёмких вызовов FFI в Deno:

// sleep.c
#ifdef _WIN32
#include 
#else
#include 
#endif

int sleep(unsigned int ms) {
  #ifdef _WIN32
  Sleep(ms);
  #else
  struct timespec ts;
  ts.tv_sec = ms / 1000;
  ts.tv_nsec = (ms % 1000) * 1000000;
  nanosleep(&ts, NULL);
  #endif
}

Вызов из Deno:

// nonblocking_ffi.ts
const library = Deno.dlopen(
  "./sleep.so",
  {
    sleep: {
      parameters: ["usize"],
      result: "void",
      nonblocking: true,
    },
  } as const,
);

library.symbols.sleep(500).then(() => console.log("After"));
console.log("Before");

Результат:

›_
$ deno run --allow-ffi unblocking_ffi.ts
Before
After

Функции обратного вызова

API FFI Deno поддерживает создание C-функций обратного вызова из функций JavaScript, чтобы динамические библиотеки могли вызывать функции Deno. Пример создания и использования таких функций:

// callback_ffi.ts
const library = Deno.dlopen(
  "./callback.so",
  {
    set_status_callback: {
      parameters: ["function"],
      result: "void",
    },
    start_long_operation: {
      parameters: [],
      result: "void",
    },
    check_status: {
      parameters: [],
      result: "void",
    },
  } as const,
);

const callback = new Deno.UnsafeCallback(
  {
    parameters: ["u8"],
    result: "void",
  } as const,
  (success: number) => {},
);

// Pass the callback pointer to dynamic library
library.symbols.set_status_callback(callback.pointer);
// Start some long operation that does not block the thread
library.symbols.start_long_operation();

// Later, trigger the library to check if the operation is done.
// If it is, this call will trigger the callback.
library.symbols.check_status();

Если функция обратного вызова UnsafeCallback выбрасывает ошибку, она передаётся функции, вызвавшей обратный вызов (в приведённом выше примере это check_status()), где её можно перехватить. Если функция обратного вызова, возвращающая значение, выбрасывает ошибку, Deno возвращает 0 (нулевой указатель для указателей) в качестве результата.

UnsafeCallback не освобождается автоматически, так как это может привести к ошибкам использования после освобождения памяти. Чтобы корректно освободить UnsafeCallback, необходимо вызвать его метод close().

const callback = new Deno.UnsafeCallback(
  { parameters: [], result: "void" } as const,
  () => {},
);

// After callback is no longer needed
callback.close();
// It is no longer safe to pass the callback as a parameter.

Нативные библиотеки также могут настраивать обработчики прерываний и напрямую вызывать из них функцию обратного вызова. Однако это не рекомендуется, поскольку может привести к неожиданным побочным эффектам и неопределённому поведению. Лучше, чтобы обработчики прерываний лишь устанавливали флаг, который позднее можно проверять, как это сделано выше с помощью check_status().

Поддерживаемые типы

Ниже приведён список типов, поддерживаемых API FFI Deno.

Тип FFI Deno C Rust
i8 number char / signed char i8
u8 number unsigned char u8
i16 number short int i16
u16 number unsigned short int u16
i32 number int / signed int i32
u32 number unsigned int u32
i64 bigint long long int i64
u64 bigint unsigned long long int u64
usize bigint size_t usize
isize bigint size_t isize
f32 number float f32
f64 number double f64
void[1] undefined void ()
pointer {} | null void * *mut c_void
buffer[2] TypedArray | null uint8_t * *mut u8
function[3] {} | null void (*fun)() Option<extern "C" fn()>
{ struct: [...] }[4] TypedArray struct MyStruct MyStruct

Начиная с Deno 1.25 тип pointer разделён на типы pointer и buffer, чтобы пользователи могли воспользоваться оптимизациями для типизированных массивов. Начиная с Deno 1.31 представление pointer в JavaScript стало непрозрачным объектом-указателем или null для нулевых указателей.

  • [1] Тип void можно использовать только в качестве типа результата.
  • [2] Тип buffer принимает типизированные массивы в качестве параметра, но в качестве результата всегда возвращает объект-указатель или null, как и тип pointer.
  • [3] Тип function работает точно так же, как тип pointer, и в качестве параметра, и в качестве типа результата.
  • [4] Тип struct используется для передачи и возврата структур C по значению (копированием). Массив struct должен перечислять тип каждого поля структуры в нужном порядке. Для структур автоматически добавляются поля выравнивания. Упакованные структуры можно определить, указав необходимое количество полей u8, чтобы избежать выравнивания. В качестве структур поддерживаются только типизированные массивы, и структуры всегда возвращаются как Uint8Array.

deno_bindgen

deno_bindgen — официальный инструмент для упрощения генерации связующего кода для библиотек Deno FFI, написанных на Rust.

Он похож на wasm-bindgen в экосистеме Rust Wasm.

Пример использования:

// mul.rs
use deno_bindgen::deno_bindgen;

#[deno_bindgen]
struct Input {
  a: i32,
  b: i32,
}

#[deno_bindgen]
fn mul(input: Input) -> i32 {
  input.a * input.b
}

Выполните deno_bindgen, чтобы сгенерировать привязки. Теперь их можно напрямую импортировать в Deno:

// mul.ts
import { mul } from "./bindings/bindings.ts";
mul({ a: 10, b: 2 }); // 20

Сообщайте обо всех проблемах, связанных с deno_bindgen, в https://github.com/denoland/deno_bindgen/issues

Жизненный цикл программы

Deno поддерживает совместимые с браузером события жизненного цикла:

  • load: срабатывает после загрузки всей страницы, включая все зависимые ресурсы, такие как таблицы стилей и изображения.
  • beforeunload: срабатывает, когда циклу событий больше нечего выполнять и программа готовится к завершению. Добавление новых асинхронных задач (например, таймеров или сетевых запросов) продолжит выполнение программы.
  • unload: срабатывает, когда программе больше нечего выполнять. Добавление новых асинхронных задач (например, таймеров или сетевых запросов) не поддерживает работу программы.
  • unhandledrejection: срабатывает, когда промис без обработчика отклонения отклонён, то есть у промиса нет обработчика .catch() или второго аргумента для .then().
  • rejectionhandled: срабатывает, когда к уже отклонённому промису добавляется обработчик .catch(). Это событие срабатывает только при наличии слушателя unhandledrejection, предотвращающего распространение события (которое привело бы к завершению программы с ошибкой).
  • error: срабатывает при возникновении неперехваченного исключения. Если зарегистрирован слушатель, он предотвращает стандартное поведение: вывод ошибки в консоль и завершение программы.

Deno также поддерживает совместимые с Node.js события жизненного цикла:

  • process.on("beforeExit"): срабатывает, когда циклу событий больше нечего выполнять и программа готовится к завершению. Добавление новых асинхронных задач (например, таймеров или сетевых запросов) продолжит выполнение программы. Аналог веб-события beforeunload. Срабатывает сразу после события beforeunload.
  • process.on("exit"): срабатывает, когда программе больше нечего выполнять. Добавление новых асинхронных задач (например, таймеров или сетевых запросов) не поддерживает работу программы. Аналог веб-события unload. Срабатывает сразу после события unload.
  • process.on("rejectionHandled"): срабатывает, когда к уже отклонённому промису добавляется обработчик .catch(). Аналог веб-события rejectionhandled. Срабатывает сразу после события rejectionhandled.
  • process.on("uncaughtException"): срабатывает, когда возникает неперехваченное исключение. Если зарегистрирован слушатель, он предотвращает стандартное поведение: вывод трассировки стека и завершение программы. Аналог веб-события error. Срабатывает сразу после события error.
  • process.on("unhandledRejection"): срабатывает, когда промис отклонён и к нему не прикреплён обработчик отклонения. Аналог веб-события unhandledrejection. Срабатывает сразу после события unhandledrejection.

Эти события можно использовать для настройки программы и очистки ресурсов.

Обработчики событий load могут быть асинхронными; выполнение программы будет ожидать их завершения. Это событие нельзя отменить. Обработчики beforeunload должны быть синхронными; их можно отменить, чтобы программа продолжила работу. Обработчики событий unload должны быть синхронными и не могут быть отменены. Те же правила действуют для соответствующих событий Node.js (beforeExit и exit).

main.ts

main.ts
import "./imported.ts";

const handler = (e: Event): void => {
  console.log(`got ${e.type} event in event handler (main)`);
};

globalThis.addEventListener("load", handler);

globalThis.addEventListener("beforeunload", handler);

globalThis.addEventListener("unload", handler);

globalThis.onload = (e: Event): void => {
  console.log(`got ${e.type} event in onload function (main)`);
};

globalThis.onbeforeunload = (e: Event): void => {
  console.log(`got ${e.type} event in onbeforeunload function (main)`);
};

globalThis.onunload = (e: Event): void => {
  console.log(`got ${e.type} event in onunload function (main)`);
};

console.log("log from main script");
imported.ts
const handler = (e: Event): void => {
  console.log(`got ${e.type} event in event handler (imported)`);
};

globalThis.addEventListener("load", handler);
globalThis.addEventListener("beforeunload", handler);
globalThis.addEventListener("unload", handler);

globalThis.onload = (e: Event): void => {
  console.log(`got ${e.type} event in onload function (imported)`);
};

globalThis.onbeforeunload = (e: Event): void => {
  console.log(`got ${e.type} event in onbeforeunload function (imported)`);
};

globalThis.onunload = (e: Event): void => {
  console.log(`got ${e.type} event in onunload function (imported)`);
};

console.log("log from imported script");

Несколько замечаний по этому примеру:

  • addEventListener и onload/onunload имеют префикс globalThis, но можно также использовать self или вовсе обойтись без префикса. Использовать window в качестве префикса не рекомендуется.
  • Для определения обработчиков событий можно использовать addEventListener и/или onload/onunload. Между ними есть существенное различие. Запустим пример:
$ deno run main.ts
log from imported script
log from main script
got load event in event handler (imported)
got load event in event handler (main)
got load event in onload function (main)
got onbeforeunload event in event handler (imported)
got onbeforeunload event in event handler (main)
got onbeforeunload event in onbeforeunload function (main)
got unload event in event handler (imported)
got unload event in event handler (main)
got unload event in onunload function (main)

Были выполнены все слушатели, добавленные с помощью addEventListener, но onload, onbeforeunload и onunload, определённые в main.ts, переопределили обработчики, заданные в imported.ts.

Иными словами, с помощью addEventListener можно зарегистрировать несколько обработчиков событий "load" или "unload", но будут выполняться только последние определённые обработчики событий onload, onbeforeunload, onunload. Поэтому, когда это возможно, предпочтительно использовать addEventListener.

beforeunload

// beforeunload.js
let count = 0;

console.log(count);

globalThis.addEventListener("beforeunload", (e) => {
  console.log("About to exit...");
  count++;
});

globalThis.addEventListener("unload", (e) => {
  console.log("Exiting");
});

count++;

© 2018–2025 the Deno authors
Licensed under the MIT License.
https://docs.deno.com/api/deno/about

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API