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

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

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. Python:每日一题001

    题目:有四个数字:1.2.3.4,能组成多少个互不相同且无重复数字的三位数?各是多少? **程序分析:可填在百位.十位.个位的数字都是1.2.3.4.组成所有的排列后再去 掉不满足条件的排列. 个人解 ...

  2. 【APP测试(Android)】--用户体验

  3. windows更改命令行cmd的字体为conlosas+微软雅黑

    windows更改命令行cmd的字体为conlosas+微软雅黑 动力来源于对美孜孜不倦的追求~ 下载conlosas+微软雅黑字体 谢谢支持. 将解压后的YaHei.Consolas.1.12.tt ...

  4. 深入理解linux关闭文件和删除文件

    背景介绍 最近看了linux系统编程(linux system programming)一书,结合深入理解linux内核(understanding the linux kernel)一书,深入理解了 ...

  5. noip第18课资料

  6. 2-postman批量执行接口

    1.postman环境设置与使用 1)点击设置,添加按钮 2)填写环境名称,参数 3)切换环境 4)使用环境变量,使用格式为:{{变量名}} 2.postman批量执行接口 1)选择要执行的文件夹,点 ...

  7. WPF触发器(Trigger) - DataTrigger

    官方文档中对DataTrigger的介绍 Represents a trigger that applies property values or performs actions when the ...

  8. 微信小程序scroll-view 横向和纵向scroll-view组件

    scroll-view为滚动视图,分为水平滚动和垂直滚动.注意滚动视图垂直滚动时一定要设置高度否则的话scroll-view不会生效.滚动视图常用的地方一般都是Item项比较多的界面,比如我的模块 主 ...

  9. IDEA中读取 resource目录下文件

    1. 资源文件 2. 加载文件 public void test() { try { System.out.println("begin test"); String filepa ...

  10. Maven - Tips

    1- Maven的Settings http://maven.apache.org/settings.html 2- Maven设置代理 示例: <proxies> <proxy&g ...