文章目录
异步
1. 什么是单线程,和异步的关系
1.1 单线程
单线程是指在JavaScript中,只有一个主线程执行代码,意味着同时只能做一件事情。如果有耗时的任务,例如循环运算或者执行alert
弹窗,会导致JS执行和DOM渲染暂时卡顿,影响用户体验。
示例代码:
// 循环运行期间, JS 执行和 DOM 渲染暂时卡顿
var i, sum = 0;
for (i = 0; i < 1000000; i++){
sum += i;
}
console.log(sum);
// alert 不处理, JS 执行 和 DOM 渲染暂时卡顿
console.log(1);
alert('hello');
console.log(2);
1.2 异步解决方案
由于单线程的限制,为了避免DOM渲染冲突,JS引入异步解决方案,让一些耗时任务在后台执行,不阻塞主线程。常见的异步操作有定时器(setTimeout
、setInterval
)、网络请求(Ajax)和事件处理(addEventListener
)等。
示例代码:
console.log(100);
setTimeout(function(){
console.log(200);
}, 1000);
console.log(300);
console.log(400);
/*
输出:
100
300
400
200
*/
异步方案虽然解决了一些问题,但也带来了新的挑战,如代码的可读性较差,回调函数嵌套导致代码难以维护等问题。
2. Event Loop(事件轮询)
2.1 什么是 Event Loop
事件轮询是JavaScript实现异步的具体解决方案。在JS中,同步代码会直接执行,异步函数会先放入异步队列中,待同步函数执行完毕后,会轮询执行异步队列中的函数。
2.2 事件轮询的过程
- 执行同步代码,直至遇到第一个异步函数。
- 将异步函数放入异步队列,等待同步代码执行完毕。
- 同步代码执行完毕后,开始轮询异步队列中的函数,逐个执行。
2.3 示例代码
console.log(100);
setTimeout(function(){
console.log(200);
}, 0); // 0ms延迟,但仍会被放入异步队列,等待同步代码执行完毕
console.log(300);
console.log(400);
/*
输出:
100
300
400
200
*/
3. 使用 JQuery 的 Deferred
3.1 JQuery 1.5 的变化
在 JQuery 1.5 之前,异步操作使用$.ajax
的回调函数进行处理,如下所示:
var ajax = $.ajax({
url: 'data.json',
success: function(){
console.log('success');
},
error: function(){
console.log('error');
}
});
console.log(ajax); // 返回一个XHR对象
而在 JQuery 1.5 之后,引入了 Deferred 对象,简化了异步操作的写法,如下所示:
var ajax = $.ajax('data.json');
ajax.done(function(){
console.log('success');
}).fail(function(){
console.log('error');
}).done(function(){
console.log('success2');
});
console.log(ajax); // 返回一个 Deferred 对象
3.2 JQuery Deferred 的特性
- JQuery Deferred 是 Promise 的前身,功能和使用方式与 Promise 类似。
- JQuery Deferred 与 Promise 可以同时使用,但不推荐混用。
4. Promise 的基本使用和原理
4.1 Promise 基本语法回顾
Promise 是 ES6 中引入的异步编程解决方案,它包含三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。基本使用如下:
const promise = new Promise((resolve, reject) => {
// 异步操作
if (/* 异步操作成功 */) {
resolve(result); // 将结果传递给 then
} else {
reject(error); // 将错误传递给 catch
}
});
promise.then((result) => {
// 处理成功的结果
}).catch((error) => {
// 处理失败的结果
});
4.2 异常捕获
使用 Promise 可以方便地捕获异步操作中的异常,通过 catch
方法捕获 reject
的结果。
4.3 多个串联
Promise 可以链式调用,实现多个异步操作的串联,提高代码的可读性和维护性。
4.4 Promise.all 和 Promise.race
Promise.all
可以接收一个由多个 Promise 组成的数组,当数组中所有的 Promise 都成功时,返回一个包含所有结果的数组;如果其中一个 Promise 失败,则返回失败的结果。Promise.race
同样接收一个 Promise 数组,但是只要有一个 Promise 状态发生变化(成功或失败),则返回该 Promise 的结果。
4.5 Promise 标准
Promise 规范是对 Promise 实现的标准,有助于不同环境下(如浏览器、Node.js)的统一使用。
5. async/await(ES7)
5.1 async/await 语法
async/await 是 ES7 中的特性,是对 Promise 的进一步封装,使异步操作更加直观和易读。使用 async/await 可以实现类似同步写法的异步操作。
示例代码:
// 最直接的同步写法
const load = async function () {
const result1 = await loadImg(src1);
console.log(result1);
const result2 = await loadImg(src2);
console.log(result2);
}
load();
5.2 async/await 特点
- 使用了 Promise,
但是不和 Promise 冲突,更加直观易读。
- 完全是同步的写法,再也没有回调函数,避免了回调地狱。
- async/await 改变不了 JavaScript 单线程、异步的本质。
6. 总结当前 JS 异步解决方案
目前,JavaScript 异步解决方案有以下几种:
- JQuery Deferred
- Promise
- async/await
其中,async/await 是最直观和易读的异步编程方式,但并不改变 JavaScript 单线程、异步的本质。虽然还有其他解决方案,如 Generator,但因为其复杂性和开发成本较高,现在更推荐使用 async/await 或其他现代的异步解决方案。