JavaScript 异步编程之回调函数

JavaScript 异步编程之回调函数

原文链接:flaviocopes.com/javascript-…

JavaScript 是同步并且是单线程的。这意味着 JavaScript 代码不能创建新的线程并且并行运行。让我们一起来看看什么是异步代码吧。

  • 编程语言中的异步
  • JavaScript
  • 回调函数
  • 处理回调函数中的错误
  • 回调的问题
  • 回调的替代方案

编程语言中的回调

计算机是异步的。

异步意味着在主流程之外,其他事件可以独立进行。在现代计算机中,每个程序都是在特定的时间槽运行的,要运行其他程序时,计算机会把当前运行中的程序停止掉。如此循环的切换运行不同的程序,看起来像是计算机同时运行着多个程序(除了多处理器计算机)。

异步在程序中非常常见,计算机会在异步事件需要执行时,才执行代码,其他时间计算机会做其他事情。例如,当一个程序正在等待网络请求的响应时,它不会阻断处理器的执行,直到请求完成。

编程语言通常是同步的,有些语言内部或者库里提供了异步处理方法,例如 C, Java, C#, PHP, Go, Ruby, Swift, Python。 一些语言通过产出新线程去处理异步。

JavaScript

JavaScript 天生是同步并且是单线程的。这意味着它不能创建新线程或者并发执行。

但是 JavaScript 是在浏览器中诞生的,它的主要工作是处理用户的动作,例如 onClick, onMouseOver, onChange, onSubmit 等动作。它如何在同步编程模型中做到这一点?

答案是浏览器环境。浏览器提供了一系列的接口处理实现这些功能。

最近,node.js引入了一个非阻塞I/O环境,把异步扩展到文件访问、网络调用等。

回调

你不可能知道用户什么时候会点击按钮,所以你只能定义一个事件处理者(event handler)处理点击事件。这个事件处理者接收一个函数,当点击时间发生的时候,这个函数就会被触发:

document.getElementById('button').addEventListener('click', () => {
    console.log('点击事件');
})
复制代码

这就是所谓的回调。

回调是一个简单的函数,它作为参数传递给另一个函数,并且在事件发生的时候会执行。在 JavaScript中,函数可以赋值给一个变量,作为其他函数的参数。

通常我们会把客户端代码包在 window 的load 事件监听里,当页面加载完毕后,回调函数才会执行:

window.addEventListener('load', () => {
    // window load
    // do what you want
})
复制代码

除了 DOM 事件中,回调被用在各种地方,最常见的例子是计时器:

setTimeout(() => {
   // 两秒后执行 
}, 2000)
复制代码

XHR 请求也接受回调函数,在这个例子中,回调函数传入了 xhr 的 onreadystatechange 方法中,当请求状态发生变化时,触发回调函数的执行:

const xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
  if (xhr.readyState === 4) {
    xhr.status === 200 ? console.log(xhr.responseText) : console.error('error')
  }
}
xhr.open('GET', 'https://yoursite.com')
xhr.send()
复制代码

回调中的错误处理

你如何处理回调中的错误呢?一个常见的策略是 Nodejs 采用的办法:任何回调函数中的第一个参数是错误对象:error-first callbacks

如果没有出错,这个对象将会是 null。如果有错,该对象会包含一些错误描述或者其他信息。

fs.readFile('/file.json', (err, data) => {
  if (err !== null) {
    //handle error
    console.log(err)
    return
  }

  //no errors, process data
  console.log(data)
})
复制代码

回调的问题

回调对于简单问题来说非常好。但是每多一次回调就回增加一层嵌套,当你有非常多的回调时,代码就回变得非常复杂:

window.addEventListener('load', () => {
  document.getElementById('button').addEventListener('click', () => {
    setTimeout(() => {
      items.forEach(item => {
        // 你的代码
      })
    }, 2000)
  })
})
复制代码

这仅仅是简单的四级代码,但我已经看到了很多级嵌套,不是很友好。

怎么解决这个问题?

回调的替代方案

从 ES6 开始, JavaScript 引入了一些特性帮助我们处理异步代码:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值