关于js异步的一些知识点
1,什么是单线程,和异步有什么关系
单线程-只有一个线程,只能做一件事
单线程的原因:避免DOM 渲染的冲突
- 浏览器需要渲染DOM
- JS 可以修改DOM 结构
- JS 执行的时候,浏览器DOM 渲染会暂停
- 两段JS 也不能同时执行(都修改DOM 就冲突了)
- webworker支持多线程,但是不能访问DOM
怎么解决:用异步,提高性能。但是异步的代码会有很多比较多难以理解的问题,比如
- 没有按照我们代码的顺序执行,可读性差
- callback中不同意模块化,易出现回调地狱。
我们平常的工作中常见的一步的函数入 setTimeout(fn,time)
,以及常见的网络请求,如 Ajax
等。
js的执行机制就是先跑完同步的代码,在轮询去读异步队列里面的函数。(等等会讲到)
如下图两个例子:
下图中的例子使用 setTimeout
来写的,所以它是一个异步函数。
输出的结果是:
100
300
400
200
下图中的例子是 ajax
的例子,也是一个异步函数,js执行的时候,会把 Ajax
放在异步队列中,等待同步代码全都执行完了再去轮询代码。
接下去我们讲讲事件轮询 (event-loop
)
2,什么是 event-loop
event-loop
是 js
异步的一种实现方式。
他的意思简单来说就是:
- 同步代码,直接执行
- 异步函数先放在异步队列中
- 待同步函数执行完毕,轮询执行异步队列的函数
我们可以具体看几个例子:
例子一
就是一个setTimeout
函数,200s
以后输出打印100。我们可以看到的是,console.log(200)
是同步代码,而setTimeout
则是一个异步函数。
setTimeout(function () { console.log(100)
})console.log(200)
所以输出结果为:
200,
100
例子二
这里我们有两个 setTimeout
,但是一个时间是0ms,另外一个是100ms。所以我们能得到图二,主进程中是 console.log(3)
,而在异步队列中的两个 setTimeout
, 0秒的立刻就被放入我们的进程中,还有一个是隔100ms被放入异步队列。
setTimeout(function () { console.log(1)
}, 1000)
setTimeout(function () { console.log(2)
})console.log(3)
结果应该是:
3,
2,
1
例子三
我们来看一个稍微复杂的函数,setTimeout
和 ajax
都存在的函数。这里的结果我们可以思考一下。
$.ajax({ url: './data.json', success: function () { console.log('a')
}
})
setTimeout(function () { console.log('b')
}, 1000)
setTimeout(function () { console.log('c')
})console.log('d')
这里应该有两种输出结果,当我们 ajax
返回的速度快于100ms的时候,那么就会先输出a
,如果 ajax
速度很慢的时候,慢于100ms,那么我们就会先输出 b
,在输出 a
。
3,是否用过 jquery 的 Deferred
简单的来讲,Deferred
就是 promise
的前世。
在开发中,我们经常遇到某些耗时很长的javascript操作。其中,既有异步的操作(比如ajax读取服务器数据),也有同步的操作(比如遍历一个大型数组),它们都不是立即能得到结果的。
通常的做法是,为它们指定回调函数(callback)。即事先规定,一旦它们运行结束,应该调用哪些函数。但是,一旦回调层级过深,处理和维护会变得相当困难。即我们常说的回调地狱。
在 JQuery
中,低于 1.5.0 版本,$.ajax()
返回的是XHR
对象,所以不能进行链式操作,如下:
$.ajax({ url: "http://localhost:8888", success: function(){ console.log("哈哈,成功了!");
}, error: function(){ console.log("出错啦!");
}
});
但是在版本 1.5.0 之后,$.ajax()
返回的是 deferred
对象,可以进行链式操作了。如下:
$.ajax("http://localhost:8888")
.done(function(){
console.log("哈哈,成功了!");
})
.fail(function(){
console.log("出错啦!");
});
看了这段代码,你是不是想起了之前我们使用过的 promise
。
其实我们无法改变 JS 异步和单线程的本质,所以为了解决回调地狱,我们只能从写法上杜绝callback 这种形式。deferred
它是一种语法糖形式,但是他是我们的代码变的更清晰。也很好的体现了,编程中的一个原则——开放封闭原则。
即对扩展开放,对修改封闭的原则。我们不需要把所有的代码都写在success
回调中了,可以把代码很好的解耦出来,便于更好的维护和测试。
下面我们来看看一个对于 deferred
的简单封装:
// 已经封装好的(A 员工)function waitHandle() { // 定义
var dtd = $.Deferred()
var wait = function (dtd) { var task = function () { console.log('执行完成')
dtd.resolve() // 成功
// dtd.reject() // 失败
}
setTimeout(task, 1000)
return dtd.promise() // wait 返回
}
return wait(dtd) // 最终返回}// 使用(B 员工)var w = waitHandle() // promise 对象$.when(w).then(function () { console.log('ok 1')
}, function () { console.log('err 1')
})
上述示例中,结果会输出
执行完成
ok1 // 1s后
这里我们讲几个要点
deferred.resolve()
和deferred.reject()
。说明其作用需要先说一下jQuery
规定deferred
对象的三种执行状态:未完成、已完成和已失败。$.when(deferreds)
方法只能接收defferred对象作为参数,所以我们要给上述函数返回一个deferred
对象我们
wait
函数返回的是dtd.promise()
,而不是简单的dtd
对象,是因为dtd
的API
可分成两类,用意不同。一类是判断是否成功这个状态, 另一个则是判断成功或者失败后,该做什么事情。这两个应该分开。 总结,dtd 的 API 可分成两类,否则后果很严重!我们可以在wait
函数返回一个dfd
。 然后在最末尾加上一句w.reject()
。输出结果为:err 1
执行完成
这个是我们需要非常注意的。因为返回
dtd.promise()
之后,我们就访问不到reject
与resolve
这两个接口了。
相关链接:jQuery的deferred对象详解,面向对象编程的6大原则,
4,Promise 的基本使用和原理
4.1 基本语法回顾
先看个简单的例子:
function loadImg(src) { var promise = new Promise(function (resolve, reject) { var img = document.createElement('img')
img. = function () {
resolve(img)
}
img. = function () {
reject('图片加载失败')
}
img.src = src
}) return promise
}var src = 'http://static.clewm.net/cli/images/cli_logo@2x.png'var result = loadImg(src)
result.then(function (img) { console.log(1, img.width) return img
}, function () { console.log('error 1')
}).then(function (img) { console.log(2, img.height)
})
上述的代码,输出的结果是:
1 300
2 54
代码很简单,就是加载一张图片,当图片加载成功的时候显示图片的宽度与宽度,我们首先 new
一个 promise
对象,然后针对图片加载成功或者失败,做一些操作。 都是一些常规的操作。
4.2 异常捕获
我们改造一下代码,loadimg
函数不变,改变下面调用的方式:
var src = 'http://static.clewm.net/cli/images/cli_logo@2x.png'var result = loadImg(src)
result.then(function (img) { console.log(1, img.width) return img
}).then(function (img) { console.log(2, img.height)
}).catch(function (ex) { // 统一捕获异常
console.log(ex)
})
这里我们规定:then
只接受一个参数,最后统一用 catch
捕获异常。所以我们then只接受一个成功之后的回调。
4.3 多个 promise
对象、Promise.all
和 Promise.race
这里我们在增加一个图片的请求。
var src1 = 'http://static.clewm.net/cli/images/cli_logo@2x.png'var result1 = loadImg(src1)var src2 = 'http://static.clewm.net/static/images/1404477720_63c377a.png?v=20150518'var result2 = loadImg(src2)
result1.then(function (img1) { console.log('第一个图片加载完成', img1.width) return result2 // 重要!!!}).then(function (img2) { console.log('第二个图片加载完成', img2.width)
}).catch(function (ex) { console.log(ex)
})
如果是多个串联,比如我们当一个 promise
返回成功之后,我们去返回另外一张图片的信息,我们可以如上代码一样,返回 return result2
。这样我们下一个 then
得到的就是图片2的信息。
Promise.all
与 Promise.race
也很好理解,他们都接受一个包含多个promise
对象的数组,前者是当所有请求都完成时,统一去执行 then
操作,而后者则是有一个请求完成就去执行 then
操作。
4.4 Promise 标准
这里就简单的提两点:
- 一个是
promise
对象的改变是不可逆的,就只有是pending
到成功或者失败,而不能反着来,同时状态也不能从成功变为失败。 - 还有一个就是
promise
必须要有一个then
方法。而且他必须接受两个参数作为参数,返回的也必须是一个promise
实例。
相关链接:Promise 迷你书,
5, 介绍一下 async/await(和 Promise 的区别、联系)
async/await
是 es7
的语法。他不是替代 primise
的一个异步解决方案,而是使用了 Promise
,并没有和 promise
冲突。
我们使用 promise
的时候,虽然看似是避免了回调地狱,但是 then
方法其实只是讲 callback
拆分了而已。
而 async/await
他的出现则是最直接的同步写法。
loadImg
方法不变,我们使用 async/await
来完成代码:
const load = async function() { const result1 = await loadImg(src1) console.log(result1); const result2 = await loadImg(src2) console.log(result2);
}
load();
因为async/await
是属于 es7
范畴的,我们需要使用 babel-polyfill
,我们可以使用 cdn
(babel-polyfill)。
这样我们也能得到和 promise
一样的结果。
关于js异步的一些知识点的更多相关文章
- js进阶 9 js操作表单知识点总结
js进阶 9 js操作表单知识点总结 一.总结 一句话总结:熟记较常用的知识点,对于一些不太常用的知识点可以在使用的时候查阅相关资料,在使用和练习中去记忆. 1.表单中学到的元素的两个对象集合石什么? ...
- node.js 需要注意知识点
复习node.js 需要注意知识点--(重点) 2.1:参数传递获取造型 客户端脚手架(发) (参数传递) node.js(收) -发ajax this.axios.get(" ...
- js异步的理解---千呼万唤始出来啊!
编译完成后(先分配给变量空间和function(){}命名的函数,var = function(){}这种函数也仅仅只是分配了个空间,还没有赋值个函数给他!),调用了若不是undefined就执行, ...
- js面试题知识点全解(一变量类型和计算)
1.js中使用typeof能得到哪些类型 2.何时使用===和== 3.js中的内置函数 4.js变量按存储方式区分为哪些类型,并描述其特点 5.如何理解json 以下对这些问题的知识点做一些总结: ...
- js面试题知识点全解(一作用域和闭包)
问题: 1.说一下对变量提升的理解 2.说明this几种不同的使用场景 3.如何理解作用域 4.实际开发中闭包的应用 知识点: js没有块级作用域只有函数和全局作用域,如下代码: if(true){ ...
- Vue.js中前端知识点总结笔记
1.框架和库的区别: 框架:framework 有着自己的语法特点.都有对应的各个模块库 library 专注于一点 框架的好处: 1.提到代码的质量,开发速度 2.提高代码的复用率 3.降低模块之间 ...
- 十四、JS同步异步知识点,重点(Node.js-fs模块补充篇)
(本片文章如果你能耐着性子看我,保证会对同步和异步有一个非常深刻的理解) JavaScript是单线程执行,所谓的单线程呢就是指如果有多个任务就必须去排队,前面任务执行完成后,后面任务再执行.因为Ja ...
- js面试题知识点全解(一作用域)
问题: 1.说一下对变量提升的理解 2.说明this几种不同的使用场景 3.如何理解作用域 4.实际开发中闭包的应用 知识点: js没有块级作用域只有函数和全局作用域,如下代码: if(true){ ...
- JS一些碎知识点
一些js基本知识点 Doctype 浏览器渲染模式 渲染模式发展历史 在多年以前(IE6诞生以前),各浏览器都处于各自比较封闭的发展中(基本没有兼容性可谈).随着WEB的发展,兼容性问题的解决越来越显 ...
随机推荐
- PYTHON指定国内PIP源
一.LINUX: vi ~/.pip/pip.conf 输入内容: [global]index-url = http://pypi.douban.com/simple/[install]trusted ...
- sqlserver如何创建链接服务器
遇到下列问题: 线上服务器A,中转服务器B,本地服务器C 数据在A上面,想在B上面操作类似 select * from [A].[database].table这样的SQL,不用去链接服务器,直接把处 ...
- 【Leetcode_easy】657. Robot Return to Origin
problem 657. Robot Return to Origin 题意: solution1: class Solution { public: bool judgeCircle(string ...
- Python-Web-数据库-mongodb
理念: ----无创建数据库方法,使用即创建 ----里面无数据,即数据库不存在 ----数据库有表,表里有一条数据,则数据库存在 ----表数据为JSON格式[{‘name’:’lisi’,’age ...
- YIIMP矿池搭建
本文将以Verge(x17)和Raven(x16rv2)为例子来说明多算法矿池YIIMP的搭建过程. 1 环境准备 1.1 准备Ubuntu 准备虚拟机或物理机,操作系统为Ubuntu 18.04,之 ...
- luoguP1463:反素数ant(打表心得☆)
题目描述 对于任何正整数x,其约数的个数记作g(x).例如g()=.g()=. 如果某个正整数x满足:g(x)>g(i) <i<x,则称x为反质数.例如,整数1,,,6等都是反质数. ...
- Nachos java版学习(一)
最近,操作系统课程设计使用伯克利大学的Nachos做为实验平台,老师也照搬伯克利的Project要求,开始我们的操作系统课程设计. 结合自己的学习过程和课设要求,我觉得对Nachos的学习首先应该从K ...
- Linux(CentOS 7)下安装postgres
事情背景:需要在Linux上安装postgres数据库,但安装目录想直接指定,所以想通过源码编译安装pg 首先下载源码安装包.源码下载地址:https://github.com/postgres/po ...
- AlgorithmMap Dev Log
Log 2019.08.29 ------------------------------------------------------------------------------------- ...
- linux下添加用户并将文件夹授权给某一个用户
### linux下添加用户并将文件夹授权给某一个用户 背景:在做一个项目时,需要外包的前端人员调试测试环境的页面,但是又不能给他服务器的账号信息,就在服务器上新添加一个子账户,再给这个账户项目文件的 ...