- 什么是单线程,和异步有什么关系
- 什么是 event-loop
- 是否用过 JQuery 的 Deferred
- Promise 的基本使用和原理
- 介绍一下 async/await(和 Promise 的区别、联系)
- 总结一下当前 JS 解决异步的方案
单线程,以及和异步的关系
单线程 - 只有一个线程,同一时间只能做一件事,两段JS不能同时执行
原因 - 为了避免 DOM 渲染冲突
-
浏览器需要渲染 DOM
-
JS 也可以修改 DOM 结构
-
JS 执行的时候,浏览器 DOM 渲染会暂停
-
两端 JS 也不能同时执行(都修改 DOM 就冲突了)
-
webworker 支持多线程,但是不能访问 DOM
异步 - 是一种“无奈”的解决方案,虽然有很多问题
// 循环运行期间,JS 执行和 DOM 渲染暂时卡顿
var i, sum = 0;
for (i = 0; i < 10000000; i++) {
sum += i;
}
console.log(sum)
// alert 不处理,JS 执行和 DOM 渲染暂时卡顿
console.log(1);
alert('hello');
console.log(2)
解决方案 - 异步
// 方案一:setTimeout
console.log(100);
setTimeout(function () {
console.log(200)
}, 1000)
console.log(300);
console.log(400); // 打印结果:100,300,400,200
// 方案二:ajax
console.log(100);
$.ajax({
url: 'XXX',
success: function(result) {
console.log(result)
}
})
console.log(300);
console.log(400); // 打印结果:100,300,400,200
event-loop 事件轮询
- 事件轮询,JS 实现异步的具体解决方案
- 同步代码,直接执行
- 异步函数先放到 异步队列 中
- 待同步函数执行完毕,轮询执行 异步队列 的函数
代码如下:
setTimeout(function() {
console.log(100)
}, 1000)
setTimeout(function() {
console.log(200)
})
console.log(300);
// ----对以上代码进行分析
// 主线程
console.log(300)
// 异步队列
// ---立刻放入异步队列
function () {
console.log(200)
}
// ---1000ms之后放入异步队列
function () {
console.log(100)
}
JQuery 的 Deferred
1、JQuery 1.5 前后 ajax 的变化
- 无法改变 JS 异步和单线程的本质
- 只能从写法上杜绝 callback 的形式
- 它是一种语法糖的形式,但是解耦了代码
- 很好的体现了 开放封闭原则 (对扩展开放,对修改封闭)
- .done(function() {});.fail(function() {});
- .then(成功的回调,失败的回调);
代码如下:
// JQuery 1.5 前
$.ajax({
url: 'XXX',
success: function() {
console.log('success1');
console.log('success2');
console.log('success3');
}
});
// JQuery 1.5 后
var ajax = $.ajax('./data.json');
ajax.done(function() {
console.log('success1')
}).fail(function() {
console.log('fail1')
}).done(function() {
console.log('success2')
})
var ajax = $ajax('./data.json');
ajax.then(function() {
console.log('success1')
}, function() {
console.log('fail1')
});
2、使用 JQuery Deferred
- dtd.resolve() 表示异步任务已经成功
- dtd.reject() 表示异步任务失败或出错
代码如下:
function waitHandle() {
var dtd = $.deferred(); // 创建一个 deferred 对象
var wait = function (dtd) {
var task = function () {
console.log('执行完成');
dtd.resolve(); // 表示异步任务已经完成
// dtd.reject(); // 表示异步任务失败或出错
}
setTimeout(task, 2000)
return dtd; // 要求返回 deferred 对象
}
// 注意,这里一定要返回
return wait(dtd);
}
// 开放封闭原则--对扩展开放对修改关闭
var w = waitHandle();
w.then(function() {
console.log('success1')
}, function() {
console.log('fail1')
})
w.then(function() {
console.log('success2')
}, function() {
console.log('fail2')
});
- dtd.promise() // 会过滤到 resolve和reject 两个"主动修改"的方法,保留 then,done,fail "被动监听"的方法
代码如下:
function waitHandle() {
var dtd = $.deferred(); // 创建一个 deferred 对象
var wait = function (dtd) {
var task = function () {
console.log('执行完成');
dtd.resolve(); // 表示异步任务已经完成
// dtd.reject(); // 表示异步任务失败或出错
}
setTimeout(task, 2000)
return dtd.promise(); //
}
// 注意,这里一定要返回
return wait(dtd);
}
// 开放封闭原则--对扩展开放对修改关闭
var w = waitHandle(); // promise对象
$.when(w).then(function() {
console.log('success1')
}, function() {
console.log('fail1')
})
Promise 的基本使用和原理
链接参考:八段代码彻底掌握 Promise
1、Promise 的语法回顾
- new Promise()
- resolve(); reject();
- .then .catch
代码如下:
var p = new Promise(function(resolve, reject){
console.log("create a promise");
resolve("success");
});
console.log("after new Promise");
p.then(function(value){
console.log(value);
});
2、异常捕获
- 两类错误:
- throw new Error('自定义错误') --- 程序逻辑之外的bug,语法错误
- reject('记载失败') 错误返回
代码如下:
// 规定: then 只接受一个参数,最后统一用 catch 捕获异常
result.then(function (img) {
console.log(1);
}).catch(function (err) {
console.log(err)
})
3、串联
result.then(function (img) {
console.log(1);
return img;
}).then(function (img) {
console.log(2);
}).catch(function (err) {
console.log(err)
})
4、Promise.all & Promise.race
- Promise.all 全部加载完成之后,统一执行
- Promise.race 只要有一个完成
代码如下:
// Promise.all 接收一个 promise 对象的数组
Promise.all([result1, result2]).then(datas => {
// datas 是一个数组,依次包含多个 promise 返回的内容
console.log(datas[0]);
console.log(datas[1]);
})
// Promise.race 接收一个包含多个 promise 对象的数组
Promise.race([result1, result2]).then(data => {
// data 即最先执行完成的 promise 的返回值
console.log(data);
})
5、Promise 标准 - 状态变化
- 三种状态:pending fulfilled(resolved) rejected
- 初始状态时 pending
- pending 变成 fulfilled,或者 pending 变成 rejected
- 状态变化是不可逆
6、Promise 标准 - then
-
then 方法返回一个新的 Promise 对象,因此可以进行链式操作
-
then方法接收两个函数作为参数,第一个参数是Promise执行成功时的回调,第二个参数是Promise执行失败时的回调。两个函数只会有一个被调用
-
Promise接收的函数参数是同步执行的,但
then方法中的回调函数执行则是异步的
代码如下:
var p = new Promise(function(resolve, reject){
resolve(1);
});
p.then(function(value){ //第一个then
console.log(value);
return value*2;
}).then(function(value){ //第二个then
console.log(value);
}).then(function(value){ //第三个then
console.log(value);
return Promise.resolve('resolve');
}).then(function(value){ //第四个then
console.log(value);
return Promise.reject('reject');
}).then(function(value){ //第五个then
console.log('resolve: '+ value);
}, function(err){
console.log('reject: ' + err);
})
输出的结果:
1
2
undefined
"resolve"
"reject: reject"
async/await
-
Aysnc 是基于 Promise 实现的
-
Promise 是对异步回调的封装
-
使用了 Promise 的封装,完全是同步的写法
-
改变不了 js 单线程,异步的本质
当前异步的解决方案
- 事件监听
- jQuery Deferred(callback)
- Promise
- Async/Await
- Generator
链接参考:JS异步编程的六种方案