首先了解什么是同步代码?什么是异步代码?同步代码和异步代码的关系是什么呢?
代码单线执行,发送服务器请求后,等待返回数据,会出现网页空白(阻塞网页运行)(通俗讲,就是代码按照顺序执行,但凡上面代码没有执行完,下边代码不能执行)
代码发送请求后继续执行后续代码,不等待服务器返回数据(网页运行流畅) js中常见的异步执行代码: 1.ajax请求:异步 JavaScript 和 XML 2.定时器:间隔一段时间才会执行, 3.事件处理函数:满足事件触发条件才会执行 异步底层原理: js代码是单线执行,代码从上往下依次执行,当遇到【异步任务】时,将其储存到一个【执行队列】中,当主线的代码执行结束后,在根据服务器的处理先后顺序执行剩余的【异步任务】
2、同步代码和异步代码的关系 1、JS中执行永远是同步代码执行完,异步代码才会执行 2、异步代码存放的位置是:在栈中,堪称两个队列 3、异步代码的特点:①完全不阻塞页面的渲染和js的代码的执行
首先答案:不会打印 这段代码,setTimeout是异步线程,需要等待js引擎处理完同步代码(while语句)之后才会执行,while语句直接是个死循环,js引擎没有空闲,不会执行下面的alert,也不会插入setTimeout。所以即使这个时候你把setTimeout的时间改成0,他还是不会执行。
JavaScript引擎是单线程运行的,浏览器无论在什么时候都只且只有一个线程在运行JavaScript程序
一、浏览器的内核是多线程的,它们在内核制控下相互配合以保持同步,一个浏览器至少实现三个常驻线程:javascript引擎线程,GUI渲染线程,浏览器事件触发线程。
javascript引擎是基于事件驱动单线程执行的,JS引擎一直等待着任务队列中任务的到来,然后加以处理,浏览器无论什么时候都只有一个JS线程在运行JS程序。
GUI渲染线程负责渲染浏览器界面,当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行。但需要注意 GUI渲染线程与JS引擎是互斥的,当JS引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。
事件触发线程,当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理。这些事件可来自JavaScript引擎当前执行的代码块如setTimeOut、也可来自浏览器内核的其他线程如鼠标点击、AJAX异步请求等,但由于JS的单线程关系所有这些事件都得排队等待JS引擎处理。(当线程中没有执行任何同步代码的前提下才会执行异步代码)
第一个参数:需要等待执行的内容,回调函数 第二个参数:等待时间以ms为单位(1000ms=1s) 返回值:定时器的编号
第一个参数:需要等待执行的内容,回调函数 第二个参数:间隔时间(1000ms=1s),每隔间隔时间执行一次(同步代码执行完之后) 返回值:
参数;①接收定时器的编号
参数;①接收定时器的编号②定时器的结束时间/定时器的执行时长
<body> <button id="btn1">Btn1</button> <button id="btn2">Btn2</button> <script> let timer = null let btn1 = document.querySelector("#btn1") let btn2 = document.querySelector("#btn2") btn1.onclick = function (){ timer = setTimeout(function (){ console.log("fasfasf") },5000) } btn2.onclick = function (){ clearTimeout(timer) } </script> </body>规定每一秒进行打印一个i++,直到第十秒停止用setInterval
// 方法一:每执行一次定时器内部都进行判断,当10s的时候进行清楚 let i = 0 let timer = setInterval(function (){ console.log(i++) if(i>10){ clearInterval(timer) } },1000) //方法二:直接设置一个setInterval定时器,然后设置一个清除定时器,10s后清除 let j=0 let timer2 = setInterval(function (){ console.log(j++) })setInterval以指定时间为周期循环执行,代码如下:
function refreshQuery(){ $("#mainTable").datagrid('reload',null); } //实时刷新时间单位为毫秒 setInterval('refreshQuery()',8000); //每隔8秒刷新一次,停不下来 /* 刷新查询 */在使用JavaScript时,为了实现某些逻辑经常会写出层层嵌套的回调函数,如果嵌套过多,会极大影响代码可读性和逻辑,这种情况也被成为回调地狱。比如说你要把一个函数 A 作为回调函数,但是该函数又接受一个函数 B 作为参数,甚至 B 还接受 C 作为参数使用,就这样层层嵌套,人称之为回调地狱,代码阅读性非常差。比如:
var sayhello = function (name, callback) { setTimeout(function () { console.log(name); callback(); }, 1000); } sayhello("first", function () { sayhello("second", function () { sayhello("third", function () { console.log("end"); }); }); }); //输出: first second third end解决回调地狱有很多方法,比如:Promise 对象、Generator 函数、async 函数
采用链式的 then,可以指定一组按照次序调用的回调函数。这时,前一个 then 里的一个回调函数,返回的可能还是一个 Promise对象(即有异步操作),这时后一个回调函数,就会等待该 Promise对象的状态发生变化,才
会被调用。由此实现异步操作按照次序执行。 var sayhello = function (name) { return new Promise(function (resolve, reject) { setTimeout(function () { console.log(name); resolve(); //在异步操作执行完后执行 resolve() 函数 }, 1000); }); } sayhello("first").then(function () { return sayhello("second"); //仍然返回一个 Promise 对象 }).then(function () { return sayhello("third"); }).then(function () { console.log('end'); }).catch(function (err) { console.log(err); }) //输出:first second third end上面代码中,第一个 then 方法指定的回调函数,返回的是另一个Promise对象。这时,第二个then方法指定的回调函数,就会等待这个新的Promise对象状态发生变化。如果变为resolved,就继续执行第二个 then 里的回调函数
resolve表示代码执行成功,reject表示代码执行失败。Promise是一个构造函数,构造函数创建对象的时候参数是一个回调函数,里边的参数是resolve和reject,这两个都是函数。 1、接收一个回调函数(参数) 2、回调函数是第一段异步操作 3、回调函数有resolve和reject两个函数形参 4、resolve,意味着紧挨着的then执行 5、reject,意味着.catch执行
1.直接then链式调用 不多说 2.利用Promise.resolve()+forEach循环/reduce循环,将promise串成一任务队列,本质上是简化的链式调用
let a = function() { return new Promise((resolve, reject) => { setTimeout(()=>{ console.log('11111111111',) resolve('第一个异步进程') },3000) }) } let b = function(){ return new Promise((resolve, reject) => { setTimeout(()=>{ console.log('2222222222' ) resolve('第二个异步进程') },2000) }) } let c = function(){ return new Promise((resolve, reject) => { setTimeout(()=>{ console.log('3333333333333') resolve('第三个异步进程') },1000) }) }reduce构建异步队列
let promiseArr = [a, b, c] promiseArr.reduce((prev, cur)=>{ return prev.then(() => { return cur().then(res => { console.log( res) }) }) }, Promise.resolve())本质上相当于
Promise.resolve().then(res => { return new Promise((resolve, reject) => { setTimeout(()=>{ console.log('11111111111', ) resolve('第一个异步进程') },3000) }) // 只有当第一个then返回了promise对象才会接着调用下一个的then方法 }).then(() => { return new Promise((resolve, reject) => { setTimeout(()=>{ console.log('2222222222', ) resolve('第二个异步进程') },2000) }) }).then(() => { return new Promise((resolve, reject) => { setTimeout(()=>{ console.log('33333333', ) resolve('第三个异步进程') },1000) }) })forEach构建异步队列
function fun(arr){ let res = Promise.resolve() arr.forEach(item => { res = res.then(item) }) } fun([a,b,c]).then( res => { console.log(res) }) //相当于 Promise.resolve().then( a ).then( b ).then( c )3.async await实现
let arr = [a, b, c] async function fun(arr){ for(const item of arr){ const res = await item().then() console.log(res) } } fun(arr)案例
//===========做饭============== function cook(){ console.log('开始做饭。'); var p = new Promise(function(resolve, reject){ //做一些异步操作 setTimeout(function(){ console.log('做饭完毕!'); resolve('鸡蛋炒饭'); }, 1000); }); return p; } //===========吃饭============================= function eat(data){ console.log('开始吃饭:' + data); var p = new Promise(function(resolve, reject){ //做一些异步操作 setTimeout(function(){ console.log('吃饭完毕!'); resolve('一块碗和一双筷子'); }, 2000); }); return p; } cook() .then(function(data){ //===============通过抛出异常的方式将cook的状态设置为reject================ throw new Error('米饭被打翻了!'); eat(data); }) .catch(function(data){ //=============捕获reject状态后的异常信息============== console.log(data); });