RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1597225
Accepted
eccs0103
eccs0103
Asked:2024-10-19 22:59:03 +0000 UTC2024-10-19 22:59:03 +0000 UTC 2024-10-19 22:59:03 +0000 UTC

异步操作以无限循环结束

  • 772

描述

情况很混乱,我会尽力详细解释一下。

第 1 部分

首先,我创建一个创建者Promise函数AbortSignal:

/**
 * @template T
 * @param {(signal: AbortSignal, resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void} callback
 * @returns {Promise<T>}
 */
Promise.withSignal = async function (callback) {
    const controller = new AbortController();
    const { promise, resolve, reject } = Promise.withResolvers();
    try {
        callback(controller.signal, resolve, reject);
        return await promise;
    } finally {
        controller.abort();
    }
};

请注意,该函数保证完成后Promise,signal状态将为aborted.

第2部分

然后我创建一个异步确认:

const dialogConfirm = document.querySelector(`dialog.pop-up.confirm`);
dialogConfirm.addEventListener(`click`, (event) => {
    if (event.target === dialogConfirm) dialogConfirm.close();
});

/**
 * @returns {Promise<boolean>} 
 */
Window.prototype.confirmAsync = async function () {
    dialogConfirm.showModal();

    const buttonAccept = dialogConfirm.querySelector(`button.highlight`);
    const buttonDecline = dialogConfirm.querySelector(`button.invalid`);

    try {
        return await Promise.withSignal((signal, resolve, reject) => {
            buttonAccept.addEventListener(`click`, (event) => resolve(true), { signal });
            buttonDecline.addEventListener(`click`, (event) => resolve(false), { signal });
            dialogConfirm.addEventListener(`close`, (event) => resolve(false), { signal });
        });
    } finally {
        dialogConfirm.close();
    }
};

我们假设 HTML 附加到页面上

第三部分

下面我创建一个“生成”的类Promise:

/**
 * @template T
 */
class PromiseFactory {
    /**
     * @param {(resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void} executor 
     */
    constructor(executor) {
        this.#executor = executor;
    }
    /** @type {(resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void} */
    #executor;
    /**
     * @returns {Promise<T>}
     */
    async run() {
        const { promise, resolve, reject } = Promise.withResolvers();
        await this.#executor.call(promise, resolve, reject);
        return promise;
    }
    /**
     * @param {(value: T) => boolean} predicate 
     */
    async runUntil(predicate) {
        while (true) {
            try {
                const result = await this.run();
                if (!predicate(result)) continue;
                return result;
            } catch {
                continue;
            }
        }
    }
}

第 4 部分

最后我把它们放在一起:

/** @type {PromiseFactory<boolean>} */
const factory = new PromiseFactory(async (resolve) => resolve(await window.confirmAsync()));

factory.runUntil(result => {
    console.log(result);
    return result;
});

这个设计的重点是创造一些可以“重复”Promise直到达到预期结果的东西。好像这段代码到现在为止还应该打开对话框,直到您单击“接受”。


完整的工作示例:

/**
 * @template T
 * @param {(signal: AbortSignal, resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void} callback
 * @returns {Promise<T>}
 */
Promise.withSignal = async function (callback) {
  const controller = new AbortController();
  const { promise, resolve, reject } = Promise.withResolvers();
  try {
    callback(controller.signal, resolve, reject);
    return await promise;
  } finally {
    controller.abort();
  }
};

const dialogConfirm = document.querySelector(`dialog.pop-up.confirm`);
dialogConfirm.addEventListener(`click`, (event) => {
  if (event.target === dialogConfirm) dialogConfirm.close();
});

/**
 * @returns {Promise<boolean>} 
 */
Window.prototype.confirmAsync = async function () {
  dialogConfirm.showModal();

  const buttonAccept = dialogConfirm.querySelector(`button.highlight`);
  const buttonDecline = dialogConfirm.querySelector(`button.invalid`);

  try {
    return await Promise.withSignal((signal, resolve, reject) => {
      buttonAccept.addEventListener(`click`, (event) => resolve(true), { signal });
      buttonDecline.addEventListener(`click`, (event) => resolve(false), { signal });
      dialogConfirm.addEventListener(`close`, (event) => resolve(false), { signal });
    });
  } finally {
    dialogConfirm.close();
  }
};

/**
 * @template T
 */
class PromiseFactory {
  /**
   * @param {(resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void} executor 
   */
  constructor(executor) {
    this.#executor = executor;
  }
  /** @type {(resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void} */
  #executor;
  /**
   * @returns {Promise<T>}
   */
  async run() {
    const { promise, resolve, reject } = Promise.withResolvers();
    await this.#executor.call(promise, resolve, reject);
    return promise;
  }
  /**
   * @param {(value: T) => boolean} predicate 
   */
  async runUntil(predicate) {
    while (true) {
      try {
        const result = await this.run();
        if (!predicate(result)) continue;
        return result;
      } catch {
        continue;
      }
    }
  }
}

/** @type {PromiseFactory<boolean>} */
const factory = new PromiseFactory(async (resolve) => resolve(await window.confirmAsync()));

factory.runUntil(result => {
  console.log(result);
  return result;
});
<dialog class="pop-up confirm">
  <button class="highlight">Accept</button>
  <button class="invalid">Decline</button>
</dialog>

问题

问题是,如果按 Decline,就会开始无限循环,尽管程序是异步的并且不会冻结,但它仍然是错误的。
我只是不明白为什么我有一个无限循环。
我仍然不知道如何解决它。

javascript
  • 1 1 个回答
  • 46 Views

1 个回答

  • Voted
  1. Best Answer
    Grundy
    2024-10-20T09:04:35Z2024-10-20T09:04:35Z

    问题在于,在对话框关闭 ( ) 之后调用之前,信号中止没有时间删除处理程序dialogConfirm.close();。

    您可以通过切换到另一个宏任务来等待删除,例如使用延迟函数:

    function delay() {
      return new Promise(r => setTimeout(r))
    }
    

    例子:

    function delay() {
      return new Promise(r => setTimeout(r))
    }
    /**
     * @template T
     * @param {(signal: AbortSignal, resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void} callback
     * @returns {Promise<T>}
     */
    Promise.withSignal = async function(callback) {
      const controller = new AbortController();
      const {
        promise,
        resolve,
        reject
      } = Promise.withResolvers();
      try {
        callback(controller.signal, resolve, reject);
        return await promise;
      } finally {
        controller.abort();
      }
    };
    
    const dialogConfirm = document.querySelector(`dialog.pop-up.confirm`);
    dialogConfirm.addEventListener(`click`, (event) => {
      if (event.target === dialogConfirm) dialogConfirm.close();
    });
    
    /**
     * @returns {Promise<boolean>} 
     */
    Window.prototype.confirmAsync = async function() {
      dialogConfirm.showModal();
    
      const buttonAccept = dialogConfirm.querySelector(`button.highlight`);
      const buttonDecline = dialogConfirm.querySelector(`button.invalid`);
    
      try {
        return await Promise.withSignal((signal, resolve, reject) => {
          buttonAccept.addEventListener(`click`, (event) => resolve(true), {
            signal
          });
          buttonDecline.addEventListener(`click`, (event) => resolve(false), {
            signal
          });
          dialogConfirm.addEventListener(`close`, (event) => resolve(false), {
            signal
          });
        });
      } finally {
        dialogConfirm.close();
        await delay();
      }
    };
    
    /**
     * @template T
     */
    class PromiseFactory {
      /**
       * @param {(resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void} executor 
       */
      constructor(executor) {
        this.#executor = executor;
      }
      /** @type {(resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void} */
      #executor;
      /**
       * @returns {Promise<T>}
       */
      async run() {
        const {
          promise,
          resolve,
          reject
        } = Promise.withResolvers();
        await this.#executor.call(promise, resolve, reject);
        return promise;
      }
      /**
       * @param {(value: T) => boolean} predicate 
       */
      async runUntil(predicate) {
        while (true) {
          try {
            const result = await this.run();
            if (!predicate(result)) continue;
            return result;
          } catch {
            continue;
          }
        }
      }
    }
    
    /** @type {PromiseFactory<boolean>} */
    const factory = new PromiseFactory(async(resolve) => resolve(await window.confirmAsync()));
    
    factory.runUntil(result => {
      console.log(result);
      return result;
    });
    <dialog class="pop-up confirm">
      <button class="highlight">Accept</button>
      <button class="invalid">Decline</button>
    </dialog>

    • 1

相关问题

  • 第二个 Instagram 按钮的 CSS 属性

  • 由于模糊,内容不可见

  • 弹出队列。消息显示不正确

  • 是否可以在 for 循环中插入提示?

  • 如何将 JSON 请求中的信息输出到数据表 Vuetify vue.js?

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    我看不懂措辞

    • 1 个回答
  • Marko Smith

    请求的模块“del”不提供名为“default”的导出

    • 3 个回答
  • Marko Smith

    "!+tab" 在 HTML 的 vs 代码中不起作用

    • 5 个回答
  • Marko Smith

    我正在尝试解决“猜词”的问题。Python

    • 2 个回答
  • Marko Smith

    可以使用哪些命令将当前指针移动到指定的提交而不更改工作目录中的文件?

    • 1 个回答
  • Marko Smith

    Python解析野莓

    • 1 个回答
  • Marko Smith

    问题:“警告:检查最新版本的 pip 时出错。”

    • 2 个回答
  • Marko Smith

    帮助编写一个用值填充变量的循环。解决这个问题

    • 2 个回答
  • Marko Smith

    尽管依赖数组为空,但在渲染上调用了 2 次 useEffect

    • 2 个回答
  • Marko Smith

    数据不通过 Telegram.WebApp.sendData 发送

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5