diff --git a/components/modal/ActionButton.tsx b/components/_util/ActionButton.tsx
similarity index 92%
rename from components/modal/ActionButton.tsx
rename to components/_util/ActionButton.tsx
index cc3766368..fe1eb83de 100644
--- a/components/modal/ActionButton.tsx
+++ b/components/_util/ActionButton.tsx
@@ -4,6 +4,7 @@ import Button from '../button';
 import type { ButtonProps } from '../button';
 import type { LegacyButtonType } from '../button/buttonTypes';
 import { convertLegacyProps } from '../button/buttonTypes';
+import useDestroyed from './hooks/useDestroyed';
 
 const actionButtonProps = {
   type: {
@@ -32,6 +33,7 @@ export default defineComponent({
     const buttonRef = ref();
     const loading = ref(false);
     let timeoutId: any;
+    const isDestroyed = useDestroyed();
     onMounted(() => {
       if (props.autofocus) {
         timeoutId = setTimeout(() => buttonRef.value.$el?.focus());
@@ -49,7 +51,9 @@ export default defineComponent({
       loading.value = true;
       returnValueOfOnOk!.then(
         (...args: any[]) => {
-          loading.value = false;
+          if (!isDestroyed.value) {
+            loading.value = false;
+          }
           close(...args);
           clickedRef.value = false;
         },
@@ -58,7 +62,9 @@ export default defineComponent({
           // eslint-disable-next-line no-console
           console.error(e);
           // See: https://github.com/ant-design/ant-design/issues/6183
-          loading.value = false;
+          if (!isDestroyed.value) {
+            loading.value = false;
+          }
           clickedRef.value = false;
         },
       );
diff --git a/components/modal/ConfirmDialog.tsx b/components/modal/ConfirmDialog.tsx
index 7a889a208..aa77dd5be 100644
--- a/components/modal/ConfirmDialog.tsx
+++ b/components/modal/ConfirmDialog.tsx
@@ -1,7 +1,7 @@
 import classNames from '../_util/classNames';
 import type { ModalFuncProps } from './Modal';
 import Dialog from './Modal';
-import ActionButton from './ActionButton';
+import ActionButton from '../_util/ActionButton';
 import { defineComponent } from 'vue';
 import { useLocaleReceiver } from '../locale-provider/LocaleReceiver';
 import { getTransitionName } from '../_util/transition';
diff --git a/components/popconfirm/__tests__/__snapshots__/demo.test.js.snap b/components/popconfirm/__tests__/__snapshots__/demo.test.js.snap
index ef00ec92e..6756abf7f 100644
--- a/components/popconfirm/__tests__/__snapshots__/demo.test.js.snap
+++ b/components/popconfirm/__tests__/__snapshots__/demo.test.js.snap
@@ -62,3 +62,9 @@ exports[`renders ./components/popconfirm/demo/placement.vue correctly 1`] = `
   </div>
 </div>
 `;
+
+exports[`renders ./components/popconfirm/demo/promise.vue correctly 1`] = `
+<!----><button class="ant-btn ant-btn-primary" type="button">
+  <!----><span>Open Popconfirm with Promise</span>
+</button>
+`;
diff --git a/components/popconfirm/demo/index.vue b/components/popconfirm/demo/index.vue
index dd2a1fc65..55a10f904 100644
--- a/components/popconfirm/demo/index.vue
+++ b/components/popconfirm/demo/index.vue
@@ -5,6 +5,7 @@
     <icon />
     <placement />
     <dynamic-trigger />
+    <promiseVue />
   </demo-sort>
 </template>
 <script lang="ts">
@@ -13,6 +14,7 @@ import Local from './local.vue';
 import Icon from './icon.vue';
 import Placement from './placement.vue';
 import DynamicTrigger from './dynamic-trigger.vue';
+import promiseVue from './promise.vue';
 import CN from '../index.zh-CN.md';
 import US from '../index.en-US.md';
 import { defineComponent } from 'vue';
@@ -21,6 +23,7 @@ export default defineComponent({
   CN,
   US,
   components: {
+    promiseVue,
     Basic,
     Local,
     Icon,
diff --git a/components/popconfirm/demo/promise.vue b/components/popconfirm/demo/promise.vue
new file mode 100644
index 000000000..8c5d8622d
--- /dev/null
+++ b/components/popconfirm/demo/promise.vue
@@ -0,0 +1,47 @@
+<docs>
+---
+order: 7
+version: 3.0
+title:
+  zh-CN: 基于 Promise 的异步关闭 (3.0+)
+  en-US: Asynchronously close on Promise (3.0+)
+---
+
+## zh-CN
+
+点击确定后异步关闭 Popconfirm,例如提交表单。
+
+## en-US
+
+Asynchronously close a popconfirm when the OK button is pressed. For example, you can use this pattern when you submit a form.
+
+</docs>
+
+<template>
+  <a-popconfirm title="Title" @confirm="confirm" @cancel="cancel">
+    <a-button type="primary">Open Popconfirm with Promise</a-button>
+  </a-popconfirm>
+</template>
+<script lang="ts">
+import { defineComponent } from 'vue';
+import { message } from 'ant-design-vue';
+export default defineComponent({
+  setup() {
+    const confirm = (e: MouseEvent) => {
+      console.log(e);
+      return new Promise(resolve => {
+        setTimeout(() => resolve(true), 3000);
+      });
+    };
+
+    const cancel = (e: MouseEvent) => {
+      console.log(e);
+      message.error('Click on No');
+    };
+    return {
+      confirm,
+      cancel,
+    };
+  },
+});
+</script>
diff --git a/components/popconfirm/index.en-US.md b/components/popconfirm/index.en-US.md
index 331c0206d..1760946b1 100644
--- a/components/popconfirm/index.en-US.md
+++ b/components/popconfirm/index.en-US.md
@@ -24,6 +24,7 @@ The difference with the 'confirm' modal dialog is that it's more lightweight tha
 | okText | text of the Confirm button | string\|slot | `Confirm` |  |
 | okButton | custom render confirm button | slot | - | 3.0 |
 | okType | Button `type` of the Confirm button | string | `primary` |  |
+| showCancel | Show cancel button | boolean | true | 3.0 |
 | title | title of the confirmation box | string\|slot | - |  |
 | visible (v-model) | hide or show | boolean | - |  |
 | icon | customize icon of confirmation | vNode\|slot | &lt;Icon type="exclamation-circle" /&gt; |  |
diff --git a/components/popconfirm/index.tsx b/components/popconfirm/index.tsx
index 1abd400bd..afa942ceb 100644
--- a/components/popconfirm/index.tsx
+++ b/components/popconfirm/index.tsx
@@ -20,6 +20,7 @@ import { getTransitionName } from '../_util/transition';
 import { cloneVNodes } from '../_util/vnode';
 import omit from '../_util/omit';
 import { tooltipDefaultProps } from '../tooltip/Tooltip';
+import ActionButton from '../_util/ActionButton';
 
 export const popconfirmProps = () => ({
   ...abstractTooltipProps(),
@@ -36,6 +37,9 @@ export const popconfirmProps = () => ({
   icon: PropTypes.any,
   okButtonProps: PropTypes.object,
   cancelButtonProps: PropTypes.object,
+  showCancel: { type: Boolean, default: true },
+  onConfirm: Function as PropType<(e: MouseEvent) => void>,
+  onCancel: Function as PropType<(e: MouseEvent) => void>,
 });
 
 export type PopconfirmProps = Partial<ExtractPropTypes<ReturnType<typeof popconfirmProps>>>;
@@ -61,7 +65,7 @@ const Popconfirm = defineComponent({
     disabled: false,
   }),
   slots: ['title', 'content', 'okText', 'icon', 'cancelText', 'cancelButton', 'okButton'],
-  emits: ['update:visible', 'confirm', 'cancel', 'visibleChange'],
+  emits: ['update:visible', 'visibleChange'],
   setup(props: PopconfirmProps, { slots, emit, expose }) {
     onMounted(() => {
       devWarning(
@@ -71,7 +75,6 @@ const Popconfirm = defineComponent({
       );
     });
     const tooltipRef = ref();
-
     expose({
       getPopupDomNode: () => {
         return tooltipRef.value?.getPopupDomNode?.();
@@ -91,14 +94,17 @@ const Popconfirm = defineComponent({
       emit('visibleChange', value, e);
     };
 
-    const onConfirm = (e: MouseEvent) => {
+    const close = (e: MouseEvent) => {
       settingVisible(false, e);
-      emit('confirm', e);
+    };
+
+    const onConfirm = (e: MouseEvent) => {
+      return props.onConfirm?.(e);
     };
 
     const onCancel = (e: MouseEvent) => {
       settingVisible(false, e);
-      emit('cancel', e);
+      props.onCancel?.(e);
     };
 
     const onKeyDown = (e: KeyboardEvent) => {
@@ -114,9 +120,10 @@ const Popconfirm = defineComponent({
       }
       settingVisible(value);
     };
-    const { prefixCls: prefixClsConfirm, configProvider } = useConfigInject('popconfirm', props);
-    const rootPrefixCls = computed(() => configProvider.getPrefixCls());
-    const popoverPrefixCls = computed(() => configProvider.getPrefixCls('popover'));
+    const { prefixCls: prefixClsConfirm, getPrefixCls } = useConfigInject('popconfirm', props);
+    const rootPrefixCls = computed(() => getPrefixCls());
+    const popoverPrefixCls = computed(() => getPrefixCls('popover'));
+    const btnPrefixCls = computed(() => getPrefixCls('btn'));
     const [popconfirmLocale] = useLocaleReceiver('Popconfirm', defaultLocale.Popconfirm);
     const renderOverlay = () => {
       const {
@@ -127,6 +134,7 @@ const Popconfirm = defineComponent({
         okText = slots.okText?.(),
         okType,
         icon = slots.icon?.(),
+        showCancel = true,
       } = props;
       const { cancelButton, okButton } = slots;
       const cancelProps: ButtonProps = {
@@ -147,15 +155,26 @@ const Popconfirm = defineComponent({
             <div class={`${popoverPrefixCls.value}-message-title`}>{title}</div>
           </div>
           <div class={`${popoverPrefixCls.value}-buttons`}>
-            {cancelButton ? (
-              cancelButton(cancelProps)
-            ) : (
-              <Button {...cancelProps}>{cancelText || popconfirmLocale.value.cancelText}</Button>
-            )}
+            {showCancel ? (
+              cancelButton ? (
+                cancelButton(cancelProps)
+              ) : (
+                <Button {...cancelProps}>{cancelText || popconfirmLocale.value.cancelText}</Button>
+              )
+            ) : null}
             {okButton ? (
               okButton(okProps)
             ) : (
-              <Button {...okProps}>{okText || popconfirmLocale.value.okText}</Button>
+              <ActionButton
+                buttonProps={{ size: 'small', ...convertLegacyProps(okType), ...okButtonProps }}
+                actionFn={onConfirm}
+                close={close}
+                prefixCls={btnPrefixCls.value}
+                quitOnNullishReturnValue
+                emitEvent
+              >
+                {okText || popconfirmLocale.value.okText}
+              </ActionButton>
             )}
           </div>
         </div>
@@ -170,6 +189,8 @@ const Popconfirm = defineComponent({
         'cancelText',
         'okText',
         'onUpdate:visible',
+        'onConfirm',
+        'onCancel',
       ]);
       const overlayClassNames = classNames(prefixClsConfirm.value, overlayClassName);
       return (
diff --git a/components/popconfirm/index.zh-CN.md b/components/popconfirm/index.zh-CN.md
index c70cd67e1..4802b440d 100644
--- a/components/popconfirm/index.zh-CN.md
+++ b/components/popconfirm/index.zh-CN.md
@@ -23,6 +23,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/fjMCD9xRq/Popconfirm.svg
 | okText | 确认按钮文字 | string\|slot | 确定 |  |
 | okButton | 完全自定义确认按钮 | slot | - | 3.0 |
 | okType | 确认按钮类型 | string | primary |  |
+| showCancel | 是否显示取消按钮 | boolean | true | 3.0 |
 | title | 确认框的描述 | string\|slot | 无 |  |
 | visible (v-model) | 是否显示 | boolean | - |  |
 | icon | 自定义弹出气泡 Icon 图标 | vNode | &lt;Icon type="exclamation-circle" /&gt; |  |