JavaScript异步编程和回调

JavaScript默认情况下是同步的,并且是单线程的。这意味着代码无法创建新线程并不能并行运行。找出异步代码的含义以及它的外观

编程语言中的异步性

计算机在设计上是异步的。

异步意味着事情可以独立于主程序流发生。

在当前的消费计算机中,每个程序都运行特定的时间段,然后停止执行,以让另一个程序继续执行。这件事的运行周期如此之快,以至于无法察觉,我们认为我们的计算机可以同时运行许多程序,但这是一种幻想(在多处理器计算机上除外)。

程序内部使用打断,该信号被发送到处理器以引起系统的注意。

我不会深入探讨这个问题,只是要记住,异步程序是正常的,并且在需要注意之前停止执行,计算机可以同时执行其他操作。当程序正在等待来自网络的响应时,它无法在请求完成之前停止处理器。

通常,编程语言是同步的,并且某些编程语言提供了一种通过语言或通过库来管理异步性的方法。 C,Java,C#,PHP,Go,Ruby,Swift,Python,它们在默认情况下都是同步的。其中一些通过使用线程来处理异步操作,从而产生一个新进程。

JavaScript

JavaScript是同步默认情况下为单线程。这意味着代码无法创建新线程并不能并行运行。

代码行是一个接一个地依次执行的,例如:

const a = 1
const b = 2
const c = a * b
console.log(c)
doSomething()

但是JavaScript诞生于浏览器内部,一开始它的主要工作是响应用户的操作,例如onClickonMouseOveronChangeonSubmit等等。如何使用同步编程模型来做到这一点?

答案在于它的环境。这浏览器通过提供一组可以处理这种功能的API来提供一种实现方法。

最近,Node.js引入了非阻塞I / O环境,以将该概念扩展到文件访问,网络调用等。

回呼

您不知道用户何时单击按钮,所以您要做的是,为click事件定义事件处理程序。此事件处理程序接受一个函数,该函数将在触发事件时被调用:

document.getElementById('button').addEventListener('click', () => {
  //item clicked
})

这就是所谓的打回来

回调是一个简单的函数,将其作为值传递给另一个函数,并且仅在事件发生时才执行。我们这样做是因为JavaScript具有一流的功能,这些功能可以分配给变量并传递给其他功能(称为高阶函数

通常将所有客户端代码包装在一个load上的事件监听器window对象,仅在页面准备就绪时才运行回调函数:

window.addEventListener('load', () => {
  //window loaded
  //do what you want
})

回调无处不在,不仅在DOM事件中使用。

一个常见的示例是使用计时器:

setTimeout(() => {
  // runs after 2 seconds
}, 2000)

XHR请求还接受回调,在此示例中,将一个函数分配给一个属性,该属性将在发生特定事件(在这种情况下,请求的状态发生变化)时被调用:

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()

处理回调中的错误

您如何处理回调错误?一种非常常见的策略是使用Node.js所采用的方法:任何回调函数中的第一个参数是错误对象:错误优先回调

如果没有错误,则对象为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 => {
        //your code here
      })
    }, 2000)
  })
})

这只是一个简单的4层代码,但是我看到了更多的嵌套层,这很不好玩。

我们该如何解决呢?

回调的替代方法

从ES6开始,JavaScript引入了一些功能,这些功能可以帮助我们处理不涉及使用回调的异步代码:

免费下载我的JavaScript初学者手册


更多js教程: