1.单线程
概念:只有一个线程,同一时间只能做一件事
原因:避免DOM渲染的冲突
- 浏览器需要渲染DOM
- JS可以修改DOM结构
- JS执行的时候,浏览器DOM渲染会停止
- 两段JS也不能同时执行(都修改DOM就冲突了)
- webworker支持多线程,但是不能访问DOM
所以js是单线程
解决:异步
js异步存在的问题: - 没按照书写方式执行,可读性差
- callback中不容易模块化
实现方式:event-loop
2.event-loop
概念:事件轮询,时间循环(js实现异步的具体解决方案)
实现过程:
- 同步代码(不在异步函数中的),直接执行
- 异步函数先放在异步队列中
- 待同步函数执行完毕,轮训执行
3.jQuery的deferred
//jquery 1.5之前
var ajax = $.ajax({
url:'./data.json',
success:function(){
console.log('success 1');
console.log('success 2');
},
error:function(){
console.log('error');
}
});
//jquery 1.5之后
var ajax = $.ajax('./data.json');
ajax.done(function(){
console.log('success a');
}).fail(function(){
console.log('fail 1');
}).done(function(){
console.log('success b');
}).fail(function(){
console.log('fail 2');
});
jquery 1.5之后版本与之前相比:
- 无法改变JS异步和单线程的本质
- 只能从写法上杜绝callback这种形势
- 它是一种语法糖形势,但是解耦了代码
- 很好的体现了开放封闭原则(对扩展开放,对修改封闭)
可以理解为promise是由jQuery的deferred演化而来
jQuery的deferred的使用
function waitHandle(){
var dtd = $.Deferred(); // 定义一个deferred实例
//对dtd进行一些操作
var wait function(dtd){
var task = function(){
console.log('执行完成');
//成功执行
dtd.resolve();
//失败执行
// dtd.reject();
}
setTimeout(task,2000);
//wait 返回
return dtd;
}
//最终返回
return wait(dtd);
}
//调用
var w = waitHandle();
w.then(function(){ // 成功
console.log('ok 1');
}, function(){ // 失败
console.log('error 1');
}).then(function(){ // 可链式操作
console.log(ok 2);
}, function(){
console.log('error 2');
});
//注意
// 如果前边是dtd.resolve(),将打印出ok 1,ok2
// 如果前边是dtd.reject(),后边调用的将不能再链式操作,如果需要多次,需要将w.then方法分别书写,链式操作将会打印出error 1,ok2,error 2
总结
dtd的API可以分为两类
- 第一类:dtd.resolve dtd.reject 主动触发
- 第二类:dtd.then dtd.done dtd.fail 被动监听
- 这两类应该分开,不然后果很严重
**解决:**用dtd.promise()
function waitHandle(){
var dtd = $.Deferred(); // 定义一个deferred实例
//对dtd进行一些操作
var wait function(dtd){
var task = function(){
console.log('执行完成');
//成功执行
dtd.resolve();
//失败执行
// dtd.reject();
}
setTimeout(task,2000);
//wait 返回
return dtd.promise();
}
//最终返回
return wait(dtd);
}
//调用
var w = waitHandle(); //promise对象
$.when(w).then(function(){ // $.when()将w继续封装一次才能执行.then
console.log('ok 1');
}, function(){ // 失败
console.log('error 1');
}).then(function(){ // 可链式操作
console.log(ok 2);
}, function(){
console.log('error 2');
});
promise和Deferred的区别
- Deferred有主动触发的函数(API),也有被动监听的函数(API),这两种函数混合很容易被外部篡改,通过生成一个promise对象来进行隔离,promise只能被动监听,不能主动触发修改。可以理解为promise是由deferred演变而来的
4.promise
基本语法详见上一篇 普通异步加载与promise
promise异常捕获
function loadImg(src) {
const promise = new Promise(function(resolve,reject){
img.onload = function(){
resolve(img);
}
img.onerror = function(){
reject(‘图片加载失败’);
}
img.src = src;
});
return promise;
}
var src = 'http://www.baidu.com/img/bd_logo1.png';
var result = loadImg(src);
// 规定then只接受一个参数(成功的回调函数),最后统一用catch捕获异常
result.then(function(img){
console.log(img.width);
}).then(function(img){
console.log(img.height);
}).catch(function(ex){
//最后统一捕获异常
console.log(ex);
})
出现多个任务时,promise还可以串联使用
function loadImg(src) {
const promise = new Promise(function(resolve,reject){
img.onload = function(){
resolve(img);
}
img.onerror = function(){
reject(‘图片加载失败’);
}
img.src = src;
});
return promise;
}
var src1 = 'http://www.baidu.com/img/bd_logo1.png';
var result1 = loadImg(src1);
var src2 = 'http://www.baidu.com/img/bd_logo2.png';
var result2 = loadImg(src2);
// 规定then只接受一个参数(成功的回调函数),最后统一用catch捕获异常
result.then(function(img1){
console.log(‘第一个图片加载完成’);
return result2; // 返回result2,即第二个加载任务,使接下来的.then()对象变为result2
}).then(function(img2){
console.log(‘第二个图片加载完成’);
}).catch(function(ex){
//最后统一捕获异常
console.log(ex);
})
Promise.all 和 Promise.race
Promise.all 接受一个promise对象的数组,待全部完成之后,统一执行success
Promise.race 接受一个包含多个promise对象的数组,只要有一个完成,就执行success
function loadImg(src) {
const promise = new Promise(function(resolve,reject){
img.onload = function(){
resolve(img);
}
img.onerror = function(){
reject(‘图片加载失败’);
}
img.src = src;
});
return promise;
}
var src1 = 'http://www.baidu.com/img/bd_logo1.png';
var result1 = loadImg(src1);
var src2 = 'http://www.baidu.com/img/bd_logo2.png';
var result2 = loadImg(src2);
// Promise.all()两个都完毕才执行
Promise.all([result1,result2],[]).then(function(datas){ // 接受datas是一个数组,依次包含了多个promise返回的内容
console.log(‘all’,datas[0]); // 将打印出img1的img标签
console.log(‘all’,datas[1]); // 将打印出img2的img标签
});
// Promise.race() 两个只有一个完毕就执行
Promise.race([result1,result2]).then(function(data){
console.log('race',data); // data即最先执行完成的promise的返回值
});
// 这里会先输出race和先加载完毕的img标签,再输出all和两个加载完毕的img标签,因为race是只有有一个完毕就会执行,所以会比all先执行完
Promise状态变化
- 三种状态:pending fulfilled rejected
- 初始状态是pending
- 只能pending变为fulfilled,或者pending变为rejected
- 状态变化不可逆
then
- Promise实例必须实现then这个方法
- then()必须可以接受两个函数作为参数(一个是成功是的回调函数,一个是失败时的回调函数)
- then()返回的必须是一个Promise实例
async/await
- then只是将callback拆分了
- async/await是最直接的同步写法
- 使用await,函数必须用async标识
- await后面跟的是一个Promise实例
- 需要安装babel-polyfill
- 使用了Promise,并没有和Promise冲突
- 完全是同步的写法,再也没有回调函数
- 但是,任何写法的改变,都改变不了JS单线程、异步的本质
function loadImg(src) {
const promise = new Promise(function(resolve,reject){
img.onload = function(){
resolve(img);
}
img.onerror = function(){
reject(‘图片加载失败’);
}
img.src = src;
});
return promise;
}
var src1 = 'http://www.baidu.com/img/bd_logo1.png';
var src2 = 'http://www.baidu.com/img/bd_logo2.png';
const load = async function(){
const result1 = await loadImg(src1);
console.log(result1); //返回img1的img标签
const result2 = await loadImg(src2);
console.log(result2); //返回img2的img标签
}