1、编程语言中的异步
默认情况下,JavaScript是同步的,并且是单线程的。这意味着代码不能创建新的线程并并行运行。了解异步代码的含义及其外观
计算机在设计上是异步的。
异步意味着事情可以独立于主程序流发生。
在当前的消费者计算机中,每个程序都在特定的时间段内运行,然后停止执行,让另一个程序继续执行。这东西以如此之快的速度循环运行,以至于不可能被注意到。我们认为我们的计算机同时运行许多程序,但这只是一种幻觉(在多处理器机器上除外)。
程序内部使用中断,这是一种发送到处理器以引起系统注意的信号。
现在我们不讨论它的内部内容,但请记住,程序异步并在需要注意之前停止执行是正常的,这样计算机就可以同时执行其他事情。当程序正在等待来自网络的响应时,在请求完成之前,它无法停止处理器。
通常,编程语言是同步的,有些语言提供了一种管理语言中异步性或通过库来管理异步性的方法。C、 默认情况下,Java、C#、PHP、Go、Ruby、Swift和Python都是同步的。其中一些通过使用线程来处理异步操作,从而生成新的进程。
2、JavaScript
默认情况下,JavaScript是同步的,并且是单线程的。这意味着代码不能创建新的线程并并行运行。
1. // 代码行一行接一行地串行执行,例如: 2. const a = 1; 3. const b = 2; 4. const c = a * b; 5. console.log(c); 6. doSomething();
但JavaScript来源于浏览器内部,其主要工作一开始是响应用户操作,如onClick、onMouseOver、onChange、onSubmit等。它如何使用同步编程模型来做到这一点?
答案在于它所处的环境。浏览器通过提供一组可以处理此类功能的API,提供了一种实现这一点的方法。
最近,Node.js引入了一个非阻塞I/O环境,将这一概念扩展到文件访问、网络调用等。
3、回调
您无法知道用户何时要单击某个按钮。因此,您为单击事件定义了一个事件处理程序。此事件处理程序接受一个函数,该函数将在触发事件时调用:
1. document.getElementById('button').addEventListener('click', () => { 2. // item clicked 3. });
这就是所谓的回调。
回调是一个简单的函数,它作为值传递给另一个函数,并且只有在事件发生时才会执行。我们之所以能做到这一点,是因为JavaScript具有一流的函数,这些函数可以分配给变量并传递给其他函数(称为高阶函数)
通常将所有客户端代码封装在窗口对象上的加载事件侦听器中,该侦听器仅在页面准备就绪时运行回调函数:
1. window.addEventListener('load', () => { 2. // window loaded 3. // do what you want 4. });
回调在任何地方都可以使用,而不仅仅是在DOM事件中。
一个常见的例子是使用计时器:
1. setTimeout(() => { 2. // runs after 2 seconds 3. }, 2000);
XHR请求也接受回调,在本例中,通过将函数分配给在特定事件发生时将被调用的属性(在这种情况下,请求的状态会发生变化):
1. const xhr = new XMLHttpRequest(); 2. xhr.onreadystatechange = () => { 3. if (xhr.readyState === 4) { 4. xhr.status === 200 ? console.log(xhr.responseText) : 5. console.error('error'); 6. } 7. }; 8. xhr.open('GET', 'https://yoursite.com'); 9. xhr.send();
3.1在回调中处理错误
如何处理回调错误?一个非常常见的策略是使用Node.js所采用的方法:任何回调函数中的第一个参数都是error对象,然后是回调数据。
如果没有错误,则该对象为null。如果出现错误,它会包含一些错误描述和其他信息。
1. const fs = require('fs'); 2. fs.readFile('/file.json', (err, data) => { 3. if (err) { 4. // handle error 5. console.log(err); 6. return; 7. } 8. // no errors, process data 9. console.log(data); 10. });
3.2回调的问题
回调非常适合简单的案例!
然而,每次回调都会添加一定程度的嵌套,当您有很多回调时,代码会很快变得复杂:
1. window.addEventListener('load', () => { 2. document.getElementById('button').addEventListener('click', () => { 3. setTimeout(() => { 4. items.forEach(item => { 5. // your code here 6. }); 7. }, 2000); 8. }); 9. });
这只是一个简单的4级嵌套代码,但我想到了更多级别的嵌套情况。
3.2回调的替代方案
从ES6开始,JavaScript引入了几个功能,帮助我们处理不涉及使用回调的异步代码:
Promises(ES6)和 Async/Await(ES2017)。