最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 处理异步事件的三种方式

    正文概述 掘金(前端先锋)   2021-01-21   390

    在网站开发中,异步事件是项目必然需要处理的一个环节,也因为前端框架的兴起,通过框架实现的 SPA 已经是快速建构网站的标配了,一部获取数据也就成了不可或缺的一环;本文来就讲一讲 JavaScript 中异步的处理方式。

    同步?异步?

    首先当然要先理解一下同步及异步分别是指什么。

    这两个名词对于初学者来说总是让人感到困惑的,毕竟从中文字面上的意思很容易让人反过来理解,从信息科学的角度来说,[同步](https:// developer.mozilla.org/en-US/docs/Glossary/Synchronous) 指的是一件一件做事,而 异步 则是很多事情在一起并行的处理。

    比如我们去银行办理业务,在窗口前排队就是同步执行,而拿到号码先去做别的事情的就是异步执行;通过 Event Loop 的特性,在 JavaScript 处里异步事件可说是轻而易举的

    那么在 JavaScript 中处理异步事件的方法是什么呢?

    回调函数

    我们最熟悉最的就是回调函数了。例如网页与用户进行互动时注册的事件监听器,就需要接收一个回调函数;或是其他 Web API 的各种功能如 setTimeoutxhr,也都能通过传递回调函数在用户要求的时机去触发。先看一个 setTimeout 的例子:

    // callback
    function withCallback() {
      console.log('start')
      setTimeout(() => {
        console.log('callback func')
      }, 1000)
      console.log('done')
    }withCallback()
    // start
    // done
    // callback func
    

    setTimeout 被执行后,当过了指定的时间间隔之后,回调函数会被放到队列的末端,再等待事件循环处理到它。

    回调函数虽然在开发中十分常见,但也有许多难以避免的问题。例如由于函数需要被传递给其他函数,开发者难以掌控其他函数内的处理逻辑;又因为回调函数仅能配合 try … catch 捕捉错误,当异步错误发生时难以控制;另外还有最著名的“回调地狱”。

    处理异步事件的三种方式

    Promise

    幸好在 ES6 之后出现了 Promise,拯救了身陷在地狱的开发者们。其基本用法也很简单:

    function withPromise() {
      return new Promise(resolve => {
        console.log('promise func')
        resolve()
      })
    }
    withPromise()
      .then(() => console.log('then 1'))
      .then(() => console.log('then 2'))
    // promise func
    // then 1
    // then 2
    

    之前讨论 Event Loop 时没有提到的是,在HTML 5 的Web API 标准 中,Event Loop 新增了微任务队列(micro task queue),而 Promise 正是通过微任务队列来驱动它的;微任务队列的触发时机是在栈被清空时,JavaScript 引擎会先确认微任务队列有没有东西,有的话就优先执行,直到清空后才从队列拿出新任务到栈上。

    如上面的例子,当函数回传一个 Promise 时,JavaScript 引擎便会把后传入的函数放到微任务队列中,反复循环,输出了上列的结果。后续的 .then 语法会回传一个新的 Promise,参数函数则接收前一个 Promise.resolve 的结果,凭借这样函数参数传递,让开发者可以管道式的按顺序处理异步事件。

    如果在例子中加上 setTimeout 就更能清楚理解微任务与一般任务的差别:

    function withPromise() {
      return new Promise(resolve => {
        console.log('promise func')
        resolve()
      })
    }
    withPromise()
      .then(() => console.log('then 1'))
      .then(() => setTimeout(() => console.log('setTimeout'), 0))
      .then(() => console.log('then 2'))
    // promise func
    // then 1
    // then 2 -> 微任务优先执行
    // setTimeout
    

    另外,前面所说的回调函数很难处理的异步错误,也可以通过 .catch 语法来捕获。

    function withPromise() {
      return new Promise(resolve => {
        console.log('promise func')
        resolve()
      })
    }
    withPromise()
      .then(() => console.log('then 1'))
      .then(() => { throw new Error('error') })
      .then(() => console.log('then 2'))
      .catch((err) => console.log('catch:', err))
    // promise func
    // then 1
    // catch: error
    //   ...error call stack
    

    async await

    从 ES6 Promise 问世之后,异步代码从回呼地狱逐渐变成了优雅的函数式管道处理,但对于不熟悉度的开发者来说,只不过是从回调地狱变成了 Promise 地狱而已。

    在 ES8 中规范了新的 async/await,虽然只是 Promise 和 Generator Function组合在一起的语法糖,但通过 async/await 便可以将异步事件用同步语法来处理,就好像是老树开新花一样,写起来的风格与 Promise 完全不同:

    function wait(time, fn) {
      return new Promise(resolve => {
        setTimeout(() => {
          console.log('wait:', time)
          resolve(fn ? fn() : time)
        }, time)
      })
    }
    await wait(500, () => console.log('bar'))
    console.log('foo')
    // wait: 500
    // bar
    // foo
    

    通过把 setTimeout 包装成 Promise,再用 await 关键字调用,可以看到结果会是同步执行的先出现 bar,再出现 foo,也就是开头提到的将异步事件写成同步处理。

    再看一个例子:

    async function withAsyncAwait() {
      for(let i = 0; i < 5; i++) {
        await wait(i*500, () => console.log(i))
      }
    }await withAsyncAwait()
    // wait: 0
    // 0
    // wait: 500
    // 1
    // wait: 1000
    // 2
    // wait: 1500
    // 3
    // wait: 2000
    // 4
    

    代码中实现了withAsyncAwait 函数,用 for 循环及 await 关键字反复执行 wait 函数;此处执行时,循环每次会按顺序等待不同的秒数再执行下一次循环。

    在使用 async/await 时,由于 await 关键字只能在 async function 中执行,使用时务必要记得要同时使用。

    另外在用循环处理异步事件时,需要注意在 ES6 之后提供的很多 Array 方法都不支持 async/await 语法,如果这里用 forEach 取代 for,结果会变成同步执行,每隔 0.5 秒就打印出数字:

    总结

    本文简单介绍了 JavaScript 处理异步的三种方式,并通过一些简单的例子说明代码的执行顺序;呼应前面提到的事件循环,再其中加入了微任务队列的概念。希望帮你理解同步和异步的应用。

    欢迎关注我的公众号:前端先锋


    起源地下载网 » 处理异步事件的三种方式

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元