最近在看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>