参考文献:王仕军——知乎专栏前端周刊

感谢作者的热心总结,本文在理解的基础上,根据自己能力水平作了一点小小的修改,在加深自己印象的同时也希望能和各位共同进步...

1. 异步与for循环

抛出一个问题,下面的代码输出什么?

 for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
6 console.log(i);

相信绝大部分同学都能答的上,它的正确答案是立即输出5,过1秒钟后一次性输出5个5,这是一个典型的JS异步问题,首先for循环的循环体是一个异步函数,并且变量i添加到全局环境中,所以立即输出一个5,一秒钟后,异步函数setTimeout输出五次循环的结果,打印5 5 5 5 5(没有时间间隔)。

2. 闭包

现在我们把需求改一下,希望输出的结果是5 ->0,1,2,3,4, 应该怎么修改代码呢?

很明显我们可以用闭包创建一个不销毁的作用域,保证变量i每次都能正常输出。

 for(var i=0;i<5;i++){
(function(j)
{setTimeout(() => {
console.log(j); //过一秒输出 0,1,2,3,4
}, 1000)})(i)
}
console.log(i); //立即输出5

因为立即执行会造成内存泄漏不建立大量使用,那么我们还可以这样

var output = function(i){
setTimeout(()=>{
console.log(i); // 过1秒输出0,1,2,3,4
},1000)
}
for(var i=0;i<5;i++){
output(i);
}
console.log(i); //立即输出5

JS基本类型是按值传递的,我们给函数output传了一个参数,所以它就会保存每次循环的实参,所以得到的结果和采用立即执行函数的结果一致。

3. ES6语法

当然我们也可以使用ES6的语法,还记得for循环中使用let声明可以有效阻止变量添加到全局作用域吗?

 for(let i=0;i<5;i++){
setTimeout(()=>{
console.log(i) //一秒钟后同时输出0,1,2,3,4
},1000)
}
6 console.log(i) //这一行会报错,因为i只存在于for循环中

for循环中let声明有一个特点,i只在本轮循环中有效,所以每循环一个i其实都是新变量,而javaScript引擎内部会记住上一次循环的值,初始化变量i时,就在上轮循环基础上计算。

现在我们又改一下需求,希望先输出0,之后每隔一秒依次输出1,2,3,4,循环结束再输出5。

很容易想到,我们可以再增加一个定时器,定时器的时间和循环次数有关

 for(var i=0;i<5;i++){
(function(j){
setTimeout(() => {
console.log(j) //立即输出0,之后每隔1秒输出1,2,3,4
}, 1000*j);
})(i)
}
setTimeout(()=>{
console.log(i) //循环结束输出5
},1000*i)

这虽然也是个办法,但代码写着确实不太好看,异步操作我们首先就要想到Promise对象,尝试用Promise对象来改写

let tasks = [];
for(var i=0;i<5;i++){
((j)=>{
tasks.push(new Promise(
(resolve)=>{
setTimeout(() => {
console.log(j);
resolve(); //执行resolve,返回Promise处理结果
}, 1000*j);
}
))
})(i)
}
Promise.all(tasks).then(()=>{
setTimeout(() => {
console.log(i);
}, 1000); //只要把时间设为1秒
})

Promise.all返回一个Promise实例,在tasks的promise状态为resolved时回调完成,这就是我们必须要在循环体中resolve()的原因。

我们将上面的代码重新排版,让其颗粒度更小,模块化更好,简洁明了

let tasks = [];   //存放一个异步操作
let output = (i)=> //返回一个Promise对象
new Promise((resolve)=>{
setTimeout(() => {
console.log(i);
resolve();
}, 1000*i);
})
for(var i=0;i<5;i++){ //生成全部的异步操作
tasks.push(output(i))
}
Promise.all(tasks).then(()=>{ //tasks里的promise对象都为resolved调用then链的第一个回调函数
setTimeout(() => {
console.log(i)
}, 1000);
})

4. async/await优化

上次写了一篇关于async和await优化then链的博客,感兴趣的可以看看:深入理解async/await

对于then链,我们是可以进一步优化的:

let sleep = (timeountMS) => new Promise((resolve) => {
setTimeout(resolve, timeountMS);
}); (async () => { // 声明即执行的 async 函数表达式
for (var i = 0; i < 5; i++) {
await sleep(1000);
console.log(i);
}
await sleep(1000);
console.log(i);
})();

【学习笔记】JS经典异步操作,从闭包到async/await的更多相关文章

  1. js经典试题之闭包

    js经典试题之闭包 1:以下代码输出的结果是? function Foo(){ var i=0; return function(){ document.write(i++); } } var f1= ...

  2. amazeui学习笔记--js插件(UI增强)--警告框Alert

    amazeui学习笔记--js插件(UI增强)--警告框Alert 一.总结 1.警告框基本样式:用am-alert声明div容器, <div class="am-alert" ...

  3. amazeui学习笔记--js插件(UI增强4)--下拉组件Dropdown

    amazeui学习笔记--js插件(UI增强4)--下拉组件Dropdown 一.总结 1.am-dropdown(及其孩子):控制下拉列表的样式 2.data-am-dropdown(及其孩子):控 ...

  4. amazeui学习笔记--js插件(UI增强3)--折叠面板Collapse

    amazeui学习笔记--js插件(UI增强3)--折叠面板Collapse 一.总结 注意点: 1.data-am-collapse:这个东西就是展开折叠事件 2.am-collapse(包括其下属 ...

  5. amazeui学习笔记--js插件(UI增强2)--按钮交互Button

    amazeui学习笔记--js插件(UI增强2)--按钮交互Button 一.总结 1.按钮loading状态: <button type="button" class=&q ...

  6. 前端学习:学习笔记(JS部分)

    前端学习:学习笔记(JS部分) 前端学习:JS学习总结(图解)    JS的简介 JS基本语法 JS内置对象 JS的函数 JS的事件 JS的BOM JS的DOM JS的简介 新建步骤 <body ...

  7. JavaScript学习笔记——JS中的变量复制、参数传递和作用域链

    今天在看书的过程中,又发现了自己目前对Javascript存在的一个知识模糊点:JS的作用域链,所以就通过查资料看书对作用域链相关的内容进行了学习.今天学习笔记主要有这样几个关键字:变量.参数传递.执 ...

  8. [学习笔记]JS 数组Array push相关问题

    前言: 今天用写了一个二维数组,都赋值为零,然后更新其中一个值,结果和预期是不一样,会整列的相同位置都是同一个值. 1.用Chrome的控制台样例如下: arrs[2][2] =1的赋值,竟然是三个数 ...

  9. js异步编程终级解决方案 async/await

      在最新的ES7(ES2017)中提出的前端异步特性:async.await. async.await是什么 async顾名思义是“异步”的意思,async用于声明一个函数是异步的.而await从字 ...

随机推荐

  1. finereport 下拉复选框多选

  2. 【git】仓库目录下文件不加入版本控制

    如果文件未做过提交操作,在文件所在目录或上级目录新建.gitignore文本文件,将文件的相对路径写入,再将该文本文件提交,则目标文件将被git忽略. 补一个gitignore文件书写规范 2.至于已 ...

  3. Myeclipse中的tomcat项目的内存溢出

    tomcat中 内存溢出 在这里写上 -Xmx1024M -Xms1024M -XX:NewSize=128m -XX:MaxNewSize=128m -XX:PermSize=128m -XX:Ma ...

  4. Java 8 Lambda 表达式及 Stream 在集合中的用法

    简介 虽然 Java 8 已经发布有一段时间了,但是关于 Java 8 中的 Lambda 表达式最近才开始系统的学习,刚开始就被 Stream 的各种骚操作深深的吸引住了,简直漂亮的不像 Java. ...

  5. CSS3美化网页!!

    一.span标签:能让某几个文字或者某个词语凸显出来        <p>           今天是11月份的<span>第一天</span>,地铁卡不打折了  ...

  6. CQOI2018 简要题解

    破解D-H协议 列个式子会发现是BSGSBSGSBSGS的模板题,直接码就是了. 代码: #include<bits/stdc++.h> #include<tr1/unordered ...

  7. LOJ-10102(桥的判断)

    题目链接:传送门 思路:找桥就行了,条件是num[v]<low[u],pre!=v; #include<iostream> #include<cstdio> #inclu ...

  8. 走进JDK(六)------ArrayList

    对于广大java程序员来说,ArrayList的使用是非常广泛的,但是发现很多工作了好几年的程序员不知道底层是啥...这我觉得对于以后的发展是非常不利的,因为java中的每种数据结构的设计都是非常完善 ...

  9. AngularJS封装webupload实现文件夹上传

    百度的webupload没有开放api获取整个文件夹的信息.本文是二次开发webupload实现获取文件夹信息. 指令封装 /** * @license lx.ui.framework v1.0.0 ...

  10. Python之旅Day2 元组 字符串 字典 集合

    元组(tuple) 元组其实跟列表差不多,也是存一组数,与列表相比,元组一旦创建,便不能再修改,所以又叫只读列表. 语法: names = ("Wuchunwei","Y ...