问题一:同步、异步、阻塞、非阻塞,傻傻搞不清
-
同步:提交请求=》等待服务器处理=》处理完毕返回=》顺次执行功能函数(在此期间未得到明确回复之前不能处理其他函数或功能)【调用者主动等待这个调用结果】
-
异步:请求通过事件触发=》请求服务器处理同时顺次执行接下来(不需要此函数执行结果的其他函数)=》处理完毕后通知返回结果
同步和异步关注的是消息的通信机制
-
阻塞:调用结果返回之前,当前线程会被挂起,调用线程只有在得到结果之后才会返回
-
非阻塞:在不能立刻得到结果之前,该调用不会阻塞当前线程
阻塞和非阻塞关注的是程序在等待调用结果(消息、返回值)时的状态
同步、异步、阻塞、非阻塞是四种概念,可以进行随机的组合,通常情况下开发过程中遇到的是:同步阻塞,异步非阻塞的情况
问题二:前端开发中常见的同步阻塞和异步非阻塞的场景及解决方案
-
异步处理的四种常见情况:
- 回调函数,很多内置函数都支持接受回调函数来异步代码
- 事件监听,大部分
Dom操作,click事件等都是异步 - 订阅与发布,常见于
angular和vue中,比如:用on来监听事件,emit来发布事件,常用于父子组件交互 - Promise
ES6的新特性,通过resolve和reject来执行异步操作,常与async和await配合使用
-
再次强调:同步和异步是一种消息通知机制(在通知的过程中,一方是否会痴痴的坚守)(同步、异步、阻塞、非阻塞)组合而非同等
- 同步阻塞:A调用B,B处理获得结果,才返回给A。A会一直等待B的回应
- 异步非阻塞:A调用B,A仅仅是调用无需等待,有结果后在通过状态来告知,或通过回到函数进行处理
-
异步非阻塞应用场景
-
回调函数callback、通过
setTimeout函数实现超时加载function text() { //|| setTimeout(() => { //|| console.log("第一项输出"); //|| }, 3000); //|| } //|| text(); //||正常的执行逻辑应当先输出1,在输出2 console.log("第二项输出"); //||但在这里setTimeout存在异步非阻塞 //||输出结果:先输出2,再输出1
//改变输出顺序,执行同步阻塞(正常逻辑) function text(cb) { //|| setTimeout(() => { //|| console.log("第一项输出"); //|| cb && cb(); //|| }, 3000) //|| } //|| text(function () { //||通过回调函数来执行同步阻塞的顺序输出 console.log("第二项输出"); //||当存在回调函数时 }) //||在setTimeout函数内部执行回调函是的内容 -
回调地狱问题,如何解决(函数回调执行自身【递归】,多次循环产生回调地狱)
function move(ele, direction, target, cb) { //getComputedStyle是干什么的? //parseInt为什么能够将0px转换成0? let start = parseInt(window.getComputedStyle(ele, null)[direction]) let speed = (target - start) / Math.abs(target - start) * 2 //通过相减计算正负,再进行除以自身的绝对值得到正负通过乘运算赋予speed function fn() { start += speed ele.style[direction] = start + "px" if (start === target) { // 改变输出结果,执行回调函数 cb && cb(); } else { //这个函数和setTimeout实现每秒递增位移有什么区别 window.requestAnimationFrame(fn) } } fn() } let ele = document.querySelector(".box") //这里开始出现层级回调嵌套,无尽的地狱模式 move(ele, "left", 400, function () { console.log("向右运动完成"); move(ele, "top", 400, function () { console.log("向下运动完成"); move(ele, "left", 0, function () { console.log("向左运动完成"); move(ele, "top", 0, function () { console.log("向上运动完成"); }) }) }) }) -
图片或数据的懒加载【通过
Promise对象进行异步加载处理】Promise对象接受两个参数,resolve[成功返还状态信息]和reject[失败返回状态信息及错误原因]then方法同样有两个resolve和reject两个参数,但这里返回的是具体的信息function loadImg() { return new Promise((res, rej) => { //直接将加载完成后的结果存入res、rej中 let img = new Image(); img.src = "./个人职业照.jpg" img.style.width = "300px" img.onload = function () { setTimeout(() => { document.querySelector("body").appendChild(img) res("加载成功") //加载成功后的返还值 }, 1000) } img.onerror = function () { rej("加载失败") //加载失败后的返还值 } }) } // loadImg函数的return为promise对象,所有可以在其自身调用then方法获取res和rej的返还值 loadImg().then(res => { console.log(res); }, err => { console.log(err); }) -
async和await实现异步之【事件循环深度理解】-
async是Generator函数的语法糖,使用async表示同步,在函数内部使用await表示异步优点:①内置执行器,自动执行;②
async代替*,await代替yield,语义化;③返回值是一个Promise对象,可以进行对应的链式操作 -
await意思是async wait(异步等待),只能在使用async定义的函数里面使用。await会解析Promise对象的值,async会等所有的await命令的Promise对象执行完,才会发生状态改变
async function async1() { console.log("async start"); await async2(); console.log("async end"); return 'async return' } async function async2() { console.log("async2"); } console.log("script start"); //||立即执行 setTimeout(function () { //为什么最后执行? console.log("setTimeout"); }, 0) async1().then(function (message) { //执行async1函数,先输出log信息,接着执行async2函数输出async内部数据 console.log(message); //执行完async2为什么跳到Promise对象 }) new Promise(function (res) { //new对象立即执行,输出log(promise1),跳到最后script end?为什么跳走 console.log("promise1"); res(); }).then(function () { console.log("promise2"); }) console.log("script end");//以上代码输出结果 script start //主线程 立即执行 async start //setTimeout函数存在异步推入任务队列,主线程顺次执行async1函数log async2 //主线程顺次执行await async2函数,await所在行本次执行,后续为异步需等待,放入任务队列 promise1 //执行async外部的下一个函数,new Promise对象 立即打印,之后的then推入任务队列 script end //顺次执行非异步函数log("script end") async end //主线程完成后,开始执行任务队列 FIFO 第一个async内部await后面的函数, return 为then方法输出,再次被推入任务队列 promise2 //执行任务队列,promise对象的then,log("promise2") async return //接着执行第二次推入任务队列的async的then方法 res setTimeout //最后执行setTimeoutasync会被await分为两部分执行,虽为异步非阻塞,但事件循环机制未被改变(可以这样理解吗)好难理解的事件循环(
Event Loop)- JS首先判断代码是同步还是异步,同步进入主线程,异步进入任务队列
- 同步任务进入主线程后一直执行,直到主线程空闲后,才会去任务队列中查看是否有可执行的异步任务,如果有就顺次推入到主线程中执行
- 事件循环是一个先进先出(
FIFO)队列,说明回调时按照它们被加入队列的顺序执行的
-
-