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

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

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. 模板学习实践一 accumulationtraits

    // 11111.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iostream> #include &l ...

  2. A1280. 最长双回文串

    学习了回文树,地址:http://blog.csdn.net/u013368721/article/details/42100363: 这个题就是正这反着加一遍就好,一开始我想的是枚举每个位置,然后一 ...

  3. unicode 编解码记录

    unicode 万国码.世界上所有的符号都有对应的Unicode code point.一般是2个字节. 这个字节可以通过任意中方式编码为二进制,例如用来保存到文件.一般通过UTF-x(例如utf-8 ...

  4. PHP中url相关函数

    1,string urlencode(string $str)/string urldecode(string $str) urlencode将中文以及特殊字符转化为16进制,然后在每个字符前面加%: ...

  5. javascript-浏览器消息提醒

    如何让用户在浏览器最小化的状态下也能及时的收到消息提醒呢? 这个问题作为webRd是要正面面对的. 大约可分两种场景:一种是类似桌面通知的形式还有一种是类似QQ提醒(在系统任务栏闪烁随后高亮);接下来 ...

  6. 11.webview、shareSDK

    布局: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:la ...

  7. 通过Function Score Query优化Elasticsearch搜索结果(综合排序)

    在使用 Elasticsearch 进行全文搜索时,搜索结果默认会以文档的相关度进行排序,如果想要改变默认的排序规则,也可以通过sort指定一个或多个排序字段. 但是使用sort排序过于绝对,它会直接 ...

  8. Android核心技术Intent和数据存储篇

    女孩:上海站到了? 男孩:嗯呢?走向世界~ 女孩:Intent核心技术和数据存储技术? 男孩:对,今日就讲这个~ Intent是各个组件之间用来进行通信的,Intent的翻译为"意图&quo ...

  9. nginx访问问题

    root响应的路径:配置的路径(root指向的路径)+完整访问路径(location的路径)+静态文件alias响应的路径:配置路径+静态文件(去除location中配置的路径)

  10. 设置select,option文本居中

    设置select,option文本居中 可以通过 padding 属性设置内边距,使它看上去居中: select{ # 从左到右依次表示上内边距,右内边距,下内边距,左内边距: padding :0 ...