DisposableStack.prototype.move()

可用性有限

此特性不是基线特性,因为它在一些最广泛使用的浏览器中不起作用。

move() 方法是 DisposableStack 实例的一个方法,它创建一个新的 DisposableStack 实例,该实例包含与当前堆栈相同的处置器,然后将当前堆栈标记为已处置,而不调用任何处置器。

语法

js
move()

参数

无。

返回值

一个新的 DisposableStack 实例。

异常

ReferenceError

如果堆栈已被处置,则抛出异常。

描述

move() 的主要目的是能够将处置的责任从当前作用域转移出去。例如,你的函数可以声明对某些资源的所有权,并在发生错误时处置它们;如果一切顺利完成,则返回这些资源并将所有权转移给调用者。

当使用 move() 转移所有权时,调用 move() 应该是你的控制流中的最后一步,因为在你通过 move() 放弃所有权和调用者从返回值中获取所有权之间,将没有中间拥有者。

js
let resource1;

function init() {
  using disposer = new DisposableStack();
  resource1 = disposer.use(getResource1());
  // ...
  // Drop ownership immediately before returning
  return disposer.move();
}

// Pick up ownership immediately after returning
using disposer = init();
js
let resource1;

function init() {
  using disposer = new DisposableStack();
  resource1 = disposer.use(getResource1());
  // ...
  const newDisposer = disposer.move();
  // If someone adds code in between these lines and an error occurs,
  // there would be no owner to free resource1
  return newDisposer;
}

using disposer = init();

也要注意以下模式,尽管使用“好”的模式在许多情况下可能非常笨拙

js
function init() {
  using disposer = new DisposableStack();
  const resource1 = disposer.use(getResource1());
  // ...
  return { disposer: disposer.move(), resource1 };
}

const { resource1, ...rest } = init();
// If someone adds code in between these lines and an error occurs,
// there would be no owner to free resource1
using disposer = rest.disposer;

move() 也可以用于条件处置,在某些情况下你可能根本不想处置资源。例如:

js
using disposer = new DisposableStack();
const server = disposer.use(makeServer());
await server.init();
if (server.ready) {
  // Successfully initialized server; it now should live through the rest
  // of the program. Drop its disposer and don't pick it up. It will no
  // longer be disposed at all.
  disposer.move();
}
// If we reach the end of the scope without running disposer.move(),
// then server isn't ready for any reason, and we dispose its resources
// by disposing the disposer.

示例

声明堆栈的所有权

js
function consumeStack(stack) {
  using newStack = stack.move(); // newStack now owns the disposers
  console.log(stack.disposed); // true
  console.log(newStack.disposed); // false
  // newStack is disposed here immediately before the function exits
}

const stack = new DisposableStack();
console.log(stack.disposed); // false
consumeStack(stack);
console.log(stack.disposed); // true

允许资源在两个代码路径中被处置

move() 的主要用例是当你有一个或多个资源,这些资源可以立即被处置,也可以被保留以备将来使用。在这种情况下,你可以将资源放入 DisposableStack,然后在需要将资源保留以备将来使用时调用 move()

js
class PluginHost {
  #disposed = false;
  #disposables;
  #channel;
  #socket;

  constructor() {
    // Create a DisposableStack that is disposed when the constructor exits.
    // If construction succeeds, we move everything out of `disposer` and into
    // `#disposables` to be disposed later.
    using disposer = new DisposableStack();

    // Create an IPC adapter around process.send/process.on("message").
    // When disposed, it unsubscribes from process.on("message").
    this.#channel = disposer.use(new NodeProcessIpcChannelAdapter(process));

    // Create a pseudo-websocket that sends and receives messages over
    // a NodeJS IPC channel.
    this.#socket = disposer.use(new NodePluginHostIpcSocket(this.#channel));

    // If we made it here, then there were no errors during construction and
    // we can safely move the disposables out of `disposer` and into `#disposables`.
    this.#disposables = disposer.move();

    // If construction failed, then `disposer` would be disposed before reaching
    // the line above. Event handlers would be removed, allowing `#channel` and
    // `#socket` to be GC'd.
  }

  [Symbol.dispose]() {
    if (this.#disposed) {
      return;
    }
    this.#disposed = true;
    // Put `this.#disposables` into a `using` variable, so it is automatically
    // disposed when the function exits.
    using disposables = this.#disposables;

    // NOTE: we can free `#socket` and `#channel` here since they will be
    // disposed by the call to `disposables[Symbol.dispose]()`, below.
    // This isn't strictly a requirement for every disposable, but is
    // good housekeeping since these objects will no longer be useable.
    this.#socket = undefined;
    this.#channel = undefined;
    this.#disposables = undefined;
  }
}

规范

规范
ECMAScript 异步显式资源管理
# sec-disposablestack.prototype.move

浏览器兼容性

另见