-
单线程
JS是单线程,一次只能做一件事,如果同一时间有多个任务的话,这些任务需排队,前一个任务执行完才会执行后一个任务。
JS为什么是单线程,这与JS用途有很大关系,JS是浏览器脚本语言,主要用来实现与用户交互,利用JS可以实现对DOM的各种操作,如果是多线程会带来很复杂的同步问题
-
为什么需要异步
JS 为单线程同一时间只能处理同个任务,但如果前一个任务执行时间很长,如文件读取和 ajax 操作,后一个任务就不得不等着,严重影响用户体验。JS 的异步就是先挂起处于等待中的任务,先运行排在后面的任务,等到文件读取或 ajax 有了结果后再回头执行挂起的任务
异步任务指不进入主线程,而进入任务队列的任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程,如图片、音乐的加载。
-
异步机制
异步如何实现,回调和事件循环。
任务队列:异步任务不会进入主线程,而是先进入任务队列,任务队列是一个先进先出的数据结构,一个异步任务结束就会在任务队列中添加事件,即可以进入执行栈。但此时的主线程不一定有空,当主线程处理完其他任务队列有空时,就会读取任务队列,读取里面有哪些事件,前面的事件会被优先处理,如果该任务指定了回调函数,就会执行回调函数中的代码。
单线程从任务队列中读取任务是不断循环的,每次栈被清空后,都会在任务队列中的读取新的任务,这就叫做任务循环,因每个任务都由一个事件触发,因此也叫事件循环。
JS异步机制步骤
- 所有同步任务都在主线程上执行,形成执行栈
- 主线程外,还存在一个任务队里,只有异步任务有了结果,就会在任务队列中放置一个事件
- 一旦执行栈中的所有同步任务执行完毕,系统就会读取任务队列,看看里面有哪些事件,对应的异步任务结束等待状态,进入执行栈,开始执行。
- 主线程不断重复第三步
-
异步编程
-
回调函数
一个函数(回调函数)作为参数传递给另一个函数,并在函数中被调用
使用 ajax 时,我们就用到了回调函数实现的异步
var req = new XMLHttpRequest() req.open("GET",url) req.send(null) //异步任务 req.Onreadystatechange = function(){} //事件回调,得到相应之后触发该事件
还要像
setTimeout
等回调函数容易形成回调炼狱,不利于代码阅读
-
Promise
ES6.0 新增的异步对象
-
Promise 是一个构造函数,既然是构造函数,那么我们就可以 new Promise() 得到一个 Promise 实例
-
在 Promise 上,有两个函数,分别叫做 resolve (成功之后的回调函数) 和 reject (失败之后的回调函数)
-
在 Promise 构造函数的 Prototype 属性上,有一个 .then() 方法,也就是说,只要是 Promise 函数构造创建的实例,都可以访问到 .then方法
-
Promise 表示一个异步操作,当我们 new Promise 实例,这个实例就表示一个具体的异步操作
-
既然 Promise 创建的实例是一个异步操作,那么这个异步操作的结果,只能有两种状态:
-
状态1:异步执行成功了,需要在内部调用成功的回调函数 resolve 把结果返回给调用者
-
状态2:异步执行失败了,需要在内部调用失败的回调函数 reject 把结果返回给调用者
-
由于 Promise 实例是一个异步操作,所以内部拿到操作的结果后,无法使用 return 把操作的结果返回个调用者;这时候,只能使用回调函数的形式,来把成功或失败的结果,返回给调用者
-
-
们可以在 new 出来的 Promise 实例上调用.then() 方法,预先为这个 Promise 异步操作,指定成功和失败的回调函数
-
-
用 Promise 封装读取文件操作
function getFileByPath(fpath){ return new Promise(function (resolve,reject) { fs.readFile(fpath, 'utf-8', (err,dataStr) => { if(err) return reject(err) resolve(dataStr) }) }) } getFileByPath('./11.txt').then(function(data){ console.log(data+'访问成功') },function (err) { console.log(err.message+'访问失败') })
-