摘要:从闭包引出来的一系列问题不起眼的开始很明显,由于异步的作用。追问如果变成该怎样处理首先可以使用闭包来解决这个问题利用立即执行函数,来解决闭包造成的问题。
从闭包引出来的一系列问题 1. 不起眼的开始
for(var i = 0; i < 5; i++) { setTimeout(function() { console.log(new Date, i) }, 1000) } console.log(new Date, i)
很明显,由于异步的作用。到最后输出的结果为6个5
如果用箭头表示前后两次输出有1s的间隔,用,代表前后一起输出,那么输出结果是5->5,5,5,5,5
这个也很容易的就可以进行解释,先执行console.log(),再进行setTimeout()的异步操作。
追问1:如果变成 5 -> 0,1,2,3,4 该怎样处理?首先可以使用闭包来解决这个问题:
for(var i = 0; i < 5; i++) { (function(j) { setTimeout(function() { console.log(new Date, j) }, 1000) })(i) } console.log(new Date, i) // 5
利用立即执行函数,来解决闭包造成的问题。
此外还可以使用setTimeout的第三个参数 文档:
for(var i = 0; i < 5; i++) { setTimeout(function(j) { console.log(new Date, j) }, 1000, i) } console.log(new Date, i) // 5
可能会有很多同学采用ES6的方式来避免:
for(let i = 0; i < 5; i++) { setTimeout(function() { console.log(new Date, i) }, 1000) } console.log(new Date, i)
但是此时并不能正确输出我们想要的结果,因为let声明的 i 产生了块级作用域,导致 for 循环外面的输出不能获取的 i ,所以此时会报错 i is not defined
追问2:如果把输出变为 0->1->2->3->4->5 呢?其中一种比较容易想到的方法:
for(var i = 0; i < 5; i++) { (function(j) { setTimeout(function() { console.log(new Date, j) }, 1000*j) })(i) } setTimeout(function() { console.log(new Date, i) }, 1000*i)
但是js中定时器的触发时机是不确定的,每次循环都会产生一个异步操作之后,会有一个输出,那么完全可以使用Promise来解决这个问题。
const tasks = [] for(var i = 0; i < 5; i++) { (function(j){ tasks.push(new Promise((resolve) => { setTimeout(() => { console.log(new Date, j) resolve() }, j*1000) })) })(i) } Promise.all(tasks).then( () => { setTimeout( () => { console.log(new Date, i) }, 1000) })
将上面代码处理一下:
const tasks = [] const output = function(i) { new Promise( (resolve) => { setTimeout( () => { console.log(new Date, i) resolve() }, 1000*i) }) } for(var i = 0;i < 5; i++) { tasks.push(output(i)) } // 全部Promise执行完毕,执行最后一个输出i Promise.all(tasks).then( () => { setTimeout( () => { console.log(new Date, i) }, 1000) })追问3:使用 async / await 怎么实现
// 模拟sleep const sleep = (time) => new Promise((resolve) => { setTimeout(resolve, time); }); (async () => { // 声明即执行的 async 函数表达式 for (var i = 0; i < 5; i++) { if (i > 0) { await sleep(1000); } console.log(new Date, i); } await sleep(1000); console.log(new Date, i); })();
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/97099.html
摘要:作用域分类作用域共有两种主要的工作模型。换句话说,作用域链是基于调用栈的,而不是代码中的作用域嵌套。词法作用域词法作用域中,又可分为全局作用域,函数作用域和块级作用域。 一篇巩固基础的文章,也可能是一系列的文章,梳理知识的遗漏点,同时也探究很多理所当然的事情背后的原理。 为什么探究基础?因为你不去面试你就不知道基础有多重要,或者是说当你的工作经历没有亮点的时候,基础就是检验你好坏的一项...
摘要:从最开始的到封装后的都在试图解决异步编程过程中的问题。为了让编程更美好,我们就需要引入来降低异步编程的复杂性。异步编程入门的全称是前端经典面试题从输入到页面加载发生了什么这是一篇开发的科普类文章,涉及到优化等多个方面。 TypeScript 入门教程 从 JavaScript 程序员的角度总结思考,循序渐进的理解 TypeScript。 网络基础知识之 HTTP 协议 详细介绍 HTT...
摘要:所以觉得把这个执行的详细过程整理一下,帮助更好的理解。类似的语法报错的如下图所示三预编译阶段代码块通过语法分析阶段之后,语法都正确的下回进入预编译阶段。另开出新文章详细分析,主要介绍执行阶段中的同步任务执行和异步任务执行机制事件循环。 一、概述 js是一种非常灵活的语言,理解js引擎的执行过程对于我们学习js是非常有必要的。看了很多这方便文章,大多数是讲的是事件循环(event loo...
阅读 755·2021-11-09 09:47
阅读 1531·2019-08-30 15:44
阅读 1124·2019-08-26 13:46
阅读 2087·2019-08-26 13:41
阅读 1242·2019-08-26 13:32
阅读 3728·2019-08-26 10:35
阅读 3498·2019-08-23 17:16
阅读 418·2019-08-23 17:07