乐趣区

关于javascript:deferpromise搞定异步弹窗组件

最近在看 vue 组件库 Plain UI 时,发现一个比拟乏味的异步弹框组件写法,操作如下:

<div class="dialog">
    <input type="text" name="message">
    <button type="button">cancel</button>
    <button type="button">confirm</button>
</div>
(async ()=>{let message = await openDialog();
    console.log("弹窗信息",message)
})()

在异步函数中关上弹窗 openDialog 办法,当用户点击 confirm 按钮后,弹窗敞开,返回输入框信息。

openDialog 办法能够很不便的通过 promise 实现,不过在看组件库源码时,发现对方是用 defer 实现的,在 promise 兼容性还不是很好的时代 JQuery 就曾经有 deferred.promise() 办法了,这里顺便也做了复习。

defer 办法:

const defer = () => {const def = {}
    def.promise = new Promise((resolve, reject) => {
      def.resolve = resolve
      def.reject = reject
    })
    return def
}

defer 办法其实返回的也是一个 promise,并且将 resolvereject 办法拆开,这样咱们就能够抉择在适当的机会调用 resolve 或者 reject 办法了。

const dialogController = () => {
  let dfd = null

  const confirmBtn = document.getElementById('confirm')
  // 点击确定按钮
  confirmBtn.addEventListener('click', () => {
  // 暗藏弹窗
    dialogEl.hide()
  // resolve 输入框信息给用户
    dfd.resolve(inputEl.value)
  })

  return () => {dfd = defer()
    dialogEl.show()
    return dfd.promise
  }
}

取得关上弹窗 promise 办法:

const openDialog = dialogController()

管制弹窗的关上,在异步函数中如果用户点击了弹窗确定按钮,敞开弹窗,取得输出信息。

const controlBtn = document.getElementById('control')
controlBtn.addEventListener('click', async () => {const message = await openDialog()
  console.log("弹窗输入框信息:",message)
})

这种形式能够不便咱们封装罕用的业务组件,之前在看 axios.cancel 源码时外面也是应用这种套路,灵便且实用。

通过 defer 形式实现的弹窗代码:

<html>
  <head>
    <title>defer promise</title>
    <style>
      .dialog {
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        display: flex;
        position: fixed;
        align-items: center;
        pointer-events: none;
        justify-content: center;
      }
      .dialog .mask {
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        position: absolute;
        opacity: 0;
        transition: 0.3s;
        background-color: rgba(0, 0, 0, 0.4);
      }

      .dialog-content {
        padding: 20px;
        transition: 0.2s;
        opacity: 0;
        transform: scale(0.95);
        background-color: #fff;
      }

      .dialog.visible {pointer-events: all;}
      .dialog.visible .mask {opacity: 1;}
      .dialog.visible .dialog-content {
        opacity: 1;
        transform: scale(1);
      }
    </style>
  </head>
  <body>
    <div class="container">
      <button id="control"> 显示弹窗 </button>
      <div class="dialog" id="dialog">
        <div class="mask" onclick="this.parentNode.classList.remove('visible')"></div>
        <div class="dialog-content">
          <input type="text" id="content" />
          <button id="confirm"> 确定 </button>
        </div>
      </div>
    </div>

    <script>
      const defer = () => {const def = {}
        def.promise = new Promise((resolve, reject) => {
          def.resolve = resolve
          def.reject = reject
        })

        return def
      }

      ;(() => {const inputEl = document.getElementById('content')
        const dialogEl = document.getElementById('dialog')
        dialogEl.show = () => dialogEl.classList.add('visible')
        dialogEl.hide = () => dialogEl.classList.remove('visible')

        const dialogController = () => {
          let dfd = null
          const confirmBtn = document.getElementById('confirm')
          confirmBtn.addEventListener('click', () => {dialogEl.hide()
            dfd.resolve(inputEl.value)
          })

          return () => {dfd = defer()
            dialogEl.show()
            return dfd.promise
          }
        }

        const openDialog = dialogController()
        const controlBtn = document.getElementById('control')
        controlBtn.addEventListener('click', async () => {const message = await openDialog()
          console.log('弹窗输入框信息:', message)
        })
      })()
    </script>
  </body>
</html>
退出移动版