day14_同步和异步
-
同步
首先,我们要知道,JavaScript的本质是一门浏览器脚本语言,在执行的时候是一行一行的执行,只有前面的代码执行完了才会执行后面的代码。
JS是单线程语言指的就是这个意思。
-
异步
一个任务是否是异步,要看程序员在编写代码的时候是否将这个任务设置为异步,而不是说时间长的任务就一定是异步的,只是通常来讲为了流畅性,编程者会将执行时间长的任务主观的设置为异步。
那么这里就涉及到一个问题了,怎么将一个任务设置为异步?
JS中最基础的有两种方式——setTimeout函数和setInterval函数。
- 循环打印“红”、“黄”、“绿” 灯
// 红灯 function first(red, yellow, green) { setTimeout(function () { console.log("红"); // yellow(green,red,yellow) second(yellow, green, red); }, 1000) } // 黄灯 // yellow(green,red,yellow) function second(yellow, green, red) { setTimeout(function () { console.log("黄"); // green(red,yellow,green) third(green, red, yellow); }, 1000) } // 绿灯 // green(red,yellow,green) function third(green, red, yellow) { setTimeout(function () { console.log("绿"); first(red, yellow, green); }, 1000) } first(second, third, first);
- 异步加载图片
class Box { constructor(time) { setTimeout(() => { var img = new Image(); img.src = "./img/36-.jpg"; img.addEventListener("load", e => { this.loadHandler(e); }) }, time) } loadHandler(e) { var evt = new Event("loadFinish"); evt.img = e.currentTarget; document.dispatchEvent(evt); } } class Ball { constructor(time) { setTimeout(() => { var img = new Image(); img.src = "./img/37.jpg"; img.addEventListener("load", e => { this.loadHandler(e); }) }, time) } loadHandler(e) { var evt = new Event("loadFinish"); evt.img = e.currentTarget; document.dispatchEvent(evt); } } class Main { arr = []; constructor() { document.addEventListener('loadFinish', e => { this.loadHandler(e); }) } loadHandler(e) { this.arr.push(e.img); if (this.arr.length === 2) { console.log(this.arr.reduce((value, item) => value + item.width, 0)); } } } var main = new Main(); var box = new Box(2000); var ball = new Ball(1000); //页面加载3秒后,显示两张图片宽度之和
-
HTML页面加载顺序
- DOM的创建 script的加载,css的加载
- 形成CSS样式树,与DOM样式树合并,形成渲染树
- 异步加载所有图片
- 全部加载完成
-
async
async 相对于页面的其余部分异步地执行(当页面继续进行解析时,脚本将被执行)
如果既不使用 async 也不使用 defer:在浏览器继续解析页面之前,立即读取并执行脚本。
注意:async 属性仅适用于外部脚本(只有在使用 src 属性时)。
-
defer
如果脚本不会改变文档的内容,可将 defer 属性加入到
script
标签中,以便加快处理文档的速度。因为浏览器知道它将能够安全地读取文档的剩余部分而不用执行脚本,它将推迟对脚本的解释,直到文档已经显示给用户为止。
注意: 只有IE支持
2.异步转同步
-
async
异步阻塞变为同步,只针对promise,对于setTimeout不起作用
async function fn(){
//async函数中return就会执行这个函数执行返回的promise的resolve方法
//因此可以在then中获取
return 1;
}
//async函数执行后就是返回一个Promise
var a=fn();
a.then(function(value){
console.log(value);
})
-
当async修饰的函数的函数体为空时
- 获取fn的值是Promise
- 状态是resolved
async function fn(){
}
console.log(fn());
//Promise {<resolved>: undefined}
- async与await
async function fn(){
console.log("a");
// 异步阻塞,变成了同步
await loadImage("./img/2-.jpg").then(function(img){
console.log(img);
});
console.log("b");
}
function loadImage(src){
return new Promise(function(resolve,reject){
let img=new Image();
img.src=src;
img.onload=function(){
resolve(img);
}
})
}
fn();
//a
//打印img
//b
- await阻断Promise的异步,等待其中的resolve
async function fn(){
console.log("aaa");
// 只有Promise的状态发生改变时才会继续向下执行await后面的内容
await new Promise(function(resolve,reject){
}).then(function(){
console.log("bbb");
}).catch(function(){
console.log("ddd");
})
console.log("ccc");
}
fn();
//aaa
- 用async和await处理promise.all()
function loadImage(src){
return new Promise(function(resolve,reject){
let img = new Image();
img.src = src;
img.onload = function(){
resolve(img);
}
})
}
class Promise1{
static async all(arr){
let list = [];
for(let i = 0; i < arr.length; i++){
//将promise异步转换为同步等待
await arr[i].then(function(img){
list.push(img);
})
}
return list;
}
}
var arr = [];
for(let i = 2; i < 80; i++){
arr.push(loadImage("./img/" + i + "-.jpg"));
}
Promise1.all(arr).then(function(list){
console.log(list);
})
- 实现循环打印 “红黄绿" 灯
function showLight(color,time){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(color);
},time)
});
}
async function show(){
await showLight("红色",2000).then(color=>console.log(color));
await showLight("黄色",2000).then(color=>console.log(color));
await showLight("绿色",2000).then(color=>{
console.log(color);
show();
});
}
show()