异步队列来实现依次执行函数【待思考】
- 要求:完成函数executeQueue(),使得执行
executeQueue(a,b,c);
时会依次执行传入的函数a、b、c。
- 重点:Promise
- 思路:我们需要实现一个队列,将这些异步函数添加进队列并且管理它们的执行,队列具有First In First Out的特性,也就是先添加进去的会被先执行,接着才会执行下一个(注意跟栈作区别)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| var a = function () { return new Promise(function (resolve, reject) { setTimeout(function () { console.log("a"); return resolve() }, 2000) }) }
var b = function () { return new Promise(function (resolve, reject) { setTimeout(function () { console.log("b"); return resolve() }, 1000) }) }
var c = function () { return new Promise(function (resolve, reject) { setTimeout(function () { console.log("c"); return resolve() }, 500) }) }
|
思路:链式调用then
可参考Promise.prototype.then()与 链式操作,可以解决问题,但不合题意,只是一个思路过渡
1 2 3 4 5 6 7 8 9
| //链式调用 a() .then(function () { return b() }) .then(function () { return c() }) // a b c
|
方法1:构建队列
- 特点:封装方法,可移植到别处使用
- forEach不能和async/await一起用,async/await要与for-of才是异步的
- then()里的函数在resolved状态下会被执行
1 2 3 4 5 6 7 8 9 10
| function queue(arr) { var sequence = Promise.resolve() arr.forEach(function (item) { sequence = sequence.then(item) }) }
queue([a, b, c])
|
注意:then里放的函数不需要()
,联系事件绑定函数进行理解。下面方法2函数猴是需要()
的。
方法2:使用async、await构建队列
- 同方法二,只是显得更高大上点
- for-of常用于异步遍历,遇到 await 会挨个串行计算,可参考“JS异步进阶”的“ES6 for-of的应用场景(与async/await)”
- 不能用forEach搭配async/await,那样会反着打印c b a
1 2 3 4 5 6
| async function queue(arr) { for (let promise of arr) { await promise() } } queue([a, b, c])
|
扩展:顺序执行需要传值的异步任务
- 场景:有a、b、c三个异步任务,要求必须先执行a,再执行b,最后执行c。且下一次任务必须要拿到上一次任务执行的结果,才能做操作
- 模拟3个异步函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| var a = function () { return new Promise(function (resolve, reject) { setTimeout(function () { resolve('a') }, 1000) }) }
var b = function (data) { return new Promise(function (resolve, reject) { resolve(data + 'b') }) }
var c = function (data) { return new Promise(function (resolve, reject) { setTimeout(function () { resolve(data + 'c') }, 500) }) }
|
思路:链式操作then
1 2 3 4 5 6 7 8 9 10 11
| //链式调用 a() .then(function (data) { return b(data) }) .then(function (data) { return c(data) }) .then(function (data) { console.log(data)// abc })
|
方法1:构建队列
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| function queue(arr) { var sequence = Promise.resolve() arr.forEach(function (item) { sequence = sequence.then(item) }) return sequence }
queue([a, b, c]) .then(data => { console.log(data) })
|
执行第一轮的时候 sequence 为a的Promise对象。
执行第二轮时, sequence 为aPromise成功时才进行的b函数最后返回的b的Promise对象,且b函数接受aPromise成功时传入的data。(b函数作为then()中的item,会自动接收aPromise成功时传入的data作为参数,所以不用另外传参!)
执行第三轮时, sequence 同上,为c的Promise对象。
最后返回的sequence是c的Promise对象
方法2:使用async、await构建队列
使用await而不是then时就要注意自行传参,这里使用res暂存bc俩函数中所传参数:
1 2 3 4 5 6 7 8 9 10 11 12
| async function queue(arr) {
let res = null for (let promise of arr) { res = await promise(res) } return await res } queue([a, b, c]) .then(data => { console.log(data) })
|
图片的懒加载
图片的懒加载也就是 延迟加载 ,图片没有出现在页面可视区域内之前都只加载一个内存小的图片用于占位,等到图片出现在页面可视区域内时才加载真正的图片。
react的react-lazy-load库
原生js
面试题:预加载 与 懒加载
- **预加载(异步加载)**:提前加载图片,当用户需要查看时可直接从本地缓存中渲染。(预加载 慕课)
- 例子: loading页面显示时就是正在提前加载众多图片的时候,等待切换到下一个页面时众多图片已经加载完毕。
- 实现方法:
- 显示loading页面时,将需要加载的图片src放入数组中
- 图片的load和error事件函数中就可以放进度条的进度计算,遍历图片数组加载图片(赋值给image对象的src)(error中也是正常进度,不然error就卡住永远无法加载结束)
- 等图片数组遍历加载结束,隐藏loading页,正常显示网页,此时所有图片已加载过一次,用户查看时会直接从本地读取
- 懒加载:懒加载的主要目的是作为服务器前端的优化,减少请求数或延迟请求数。
- 两种技术的本质:两者的行为是相反的,一个是提前加载,一个是迟缓甚至不加载。
- 懒加载对服务器前端有一定的缓解压力作用,预加载则会增加服务器前端压力。
异步进阶
描述event loop机制
- 第一遍可以只到宏任务微任务(**执行宏任务(同步任务)=》清空微任务=》清空宏任务=》如有微任务则继续清空微任务=》…**)
- 第二遍深入可加上和宏任务微任务与DOM渲染的关系
- 不要混着讲,怕乱
宏任务与微任务的区别
- 宏任务:script setTimeout setInterval ajax DOM 事件
- 除script,其他都是浏览器引擎来执行,因为宏任务都是Web API,不属于ES语法
- 微任务:Promise.then()(对于前端来说)async/await
- js引擎来执行
- 注意:Promise的函数体是定义时就立即执行的,then()是微任务。async函数体是同步,await当行代码也是同步,但await后的代码时异步(微任务)
- 宏任务比微任务执行的更早,js引擎处理速度比浏览器引擎快,所以 异步任务中微任务执行时机先于宏任务
- 微任务在DOM渲染前触发,宏任务在DOM渲染后触发
Promise的三种状态,如何变化
- pending,resolved,rejected
- pending–>resolved 或 pending–>rejected
- 变化不可逆
Promise catch 连接 then
catch执行与否的问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| Promise.resolve().then(() => { console.log(1) }).catch(() => { console.log(2) }).then(() => { console.log(3) })
Promise.resolve().then(() => { console.log(1) throw new Error('erro1') }).catch(() => { console.log(2) }).then(() => { console.log(3) })
Promise.resolve().then(() => { console.log(1) throw new Error('erro1') }).catch(() => { console.log(2) }).catch(() => { console.log(3) })
|
async/await 语法问题
1 2 3 4 5 6 7
| async function fn() { return 100 } (async function () { const a = fn() const b = await fn() })()
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| (async function () { console.log('start') const a = await 100 console.log('a', a) const b = await Promise.resolve(200) console.log('b', b) const c = await Promise.reject(300) console.log('c', c) console.log('end') })()
|
Promise 和 setTimeout 顺序
- 同步任务先于异步任务
- 宏任务(setTimeout)先于微任务(Promise.then)
- 但是同步任务也属于宏任务,所以 执行宏任务(同步任务)=》清空微任务(Promise.then)=》清空宏任务(setTimeout)
1 2 3 4 5 6 7 8 9
| console.log(100) setTimeout(() => { console.log(200) }) Promise.resolve().then(() => { console.log(300) }) console.log(400)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| console.log(100) setTimeout(() => { console.log(200) }) Promise.resolve().then(() => { console.log(300) }) setTimeout(() => { console.log(400) }) Promise.resolve().then(() => { console.log(500) }) console.log(600)
|
【重点】async/await 执行顺序问题
- 注意:
- async()调用时函数体立即执行,await当行代码为同步,await后是微任务。
- Promise 的函数体(即new Promise(){…})会立刻执行。
- then()是微任务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| async function async1 () { console.log('async1 start') await async2()
console.log('async1 end') }
async function async2 () { console.log('async2') }
console.log('script start')
setTimeout(function () { console.log('setTimeout') }, 0)
async1()
new Promise (function (resolve) { console.log('promise1') resolve() }).then (function () { console.log('promise2') })
console.log('script end')
|
宏任务(同步,async)=》清空微任务(await后,then)=》宏任务(setTimeout)
script start
async1 start
async2
promise1
script end
(同步代码执行完毕)
async1 end
promise2
setTimeout