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

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

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. centos7 mysql的安装与配置

    用yum直接安装,不用更改配置 1. #yum install mysql #yum install mysql-server #yum install mysql-devel yum运行报错的化有可 ...

  2. selenium之生成html测试报告--testng.xsl

    自制版制作步骤: 1.首先下载一个文件名为testng.xslt-1.1.zip testng.xslt-1.1我在印象笔记里面备份了一份 打开testng.xslt中lib文件夹,找到saxon-8 ...

  3. s2 Docker环境的快速搭建方法

    常规linux下安装 centos7 下配置docker源并安装 cat >/etc/yum.repos.d/docker.repo< [dockerrepo] name=Docker R ...

  4. k-th smallest 问题总结

    k-th smallest/biggest 问题大约有这几道: 373. Find K Pairs with Smallest Sums 从两个list里各取一个数求和,求所有可能的sum里第k小的 ...

  5. 带权单源最短路[稀疏图](Dijkstra)

    因为是稀疏图,所以应当选择邻接表来存储 构造一个邻接表 这只是我的构造方法,有很多种更好的构造方法,大家可以自己去构造 typedef int vertex; typedef int WeightTy ...

  6. ASP.NET MVC下使用AngularJs语言(七):Cookie的使用

    网站开发,使用Cookie对暂存数据进行读写,可以使用C#,javascript,jQuery,也可以使用angularjs等等来读写...... 本篇实现angularjs环境之下对Cookie时行 ...

  7. Interview Common Sample Codes

    1. Quick Sort: int partition(int A[], int p, int r) { int x = A[r]; // Pivot element int i = p - 1; ...

  8. 简单的Poc Exp编写(上)

    简单的POC EXP 编写 (上)   作者BY Greekn   今天主要讲的 是关于web 方面的 poc 编写 关于web 安全 个人理解的话 一个就是攻击  另一个就是漏洞挖掘了 防御的话 看 ...

  9. Spring事务管理(详解+实例)

    1 初步理解 理解事务之前,先讲一个你日常生活中最常干的事:取钱. 比如你去ATM机取1000块钱,大体有两个步骤:首先输入密码金额,银行卡扣掉1000元钱:然后ATM出1000元钱.这两个步骤必须是 ...

  10. Oracle 的分页查询 SQL 语句

    Oracle的分页查询语句基本上可以按照本文给出的格式来进行套用. 分页查询格式: SELECT * FROM (SELECT A.*, ROWNUM RN FROM (SELECT * FROM T ...