JS-同步异步理解以及 async 和 await
JavaScript语言的一大特点就是单线程,也就是说,同一时间只能做一件事。那么,为什么JavaScript不能有多个线程呢?
- JavaScript是单线程与他的用途有关。作为浏览器脚本语言,JavaScript的主要用途就是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个删除了这个节点,这时浏览器应该以哪个线程为准?
- 所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经称为了这门语言的特征,将来也不会改变。
- 坏处之一:后面的任务都必须排队等着,会拖延整个程序的执行。"同步模式"就是上一段的模式,后一个任务等待前一个任务结束,然后再执行,程序的执行顺序与任务的排列顺序是一致的、同步的;"异步模式"则完全不同,每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的。
同步和异步
- 计算机世界的 “同步和异步” 和 现实生活中的 “同步和异步” 概念不同。
- 单线程意味着,所有队伍都需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时过长,后一个任务就不得不一直等着。于是,就有了一个概念,任务队列。
- 例如 进行网络请求时,网速不行,需要很长事件才能得到结果,比如20秒才能得到结果,这是CPU是空闲的,此时任务队列不得不等待结果出来,再往下执行。
- JavaScript设计者意识到。这是主线程可以完全不管IO设备,挂起等待中的任务,先运行排在后面的任务。等到IO设备返回了结果,在回过头,把挂起的任务继续执行下去。
- 于是,所有的任务都可以分为两种。同步(synchronous)和异步(asynchronous)。
- 同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行下一个任务。
- 异步任务:不进入主线程,而进入"任务队列"(task queue)的任务只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
- 异步的目的: 不阻塞操作。
- 对于异步操作,阮一峰的解释颇为明朗 所谓 "异步",就是一个任务不是连续完成的,可以理解为被人为分成两段,先执行第一段,然后转而去执行其他任务,等做好了准备,在回过头来执行第二段。 比如,有一个任务是读取文件进行处理,任务的第一段是向操作系统发出请求,要求读取文件。然后,程序执行其他惹他任务,等到操作系统返回文件,再接着执行任务的第二段(处理文件)。这种不连续的执行,就叫做异步。
- 相应地,连续的执行就叫做同步。由于是连续执行,不能插入其他任务,所以操作系统从硬盘读取文件的这段时间,程序只能干等着
JS异步编程的方式
- 回调函数(callback)
- 事件监听 (事件驱动)
- Promise
- 发布/订阅 RxJS
回调函数(callback)
回调函数,就是把任务的第二段,单独写在一个函数里面,等到重新执行这个任务的时候就直接调用这个函数。回调函数的英文名callback ,直译过来就是"重新调用"。 例如 :读取文件,然后处理文件
fs.readFile('/et/passwd','utf-8',function(err,data){
if (err) throw err;
console.log(data);
}
这个例子中,readFile的第三个参数,就是回调函数,也就是任务的第二段,等到系统返回了/et/passwd'这个文件以后,回调函数才会执行。
假设函数 f1(),f2(),f2 需要等待f1 的执行结果,
f1()
f2()
f1() 如果是一个很耗时的任务,可以考虑改写成回调函数,把 f2 写成 f1 的回调函数。
function f1 (callback) {
setTimeout (function () {
// f1 执行逻辑,任务代码
callback ()
},1000)
}
执行代码为
f1(f2)
事件监听 基于事件驱动模式没任务的执行,不取决于代码的顺序,而是取决于某个事件是否发生。
f1.on('done',f2)
function f1 () {
setTimeout (function () {
// f1 执行逻辑,任务代码
f1.trigger('done');
},1000)
}
发布订阅模式 Rx JS Promise对象
f1().then(f2).thne(f3)
async 和 await
synchronous: 同步 asynchronous :异步 await: 等待
ES2017 引入,异步操作更方便。
// 判断事件权限分析
async getInfo () {
await this.getUserInfo()
if (this.authoritys.event_analy === 0) {
this.$router.push({
path: '/404'
})
}
}
async和await: 难点是 错误处理机制。 因为 返回的死Promise 对象,运行结果可能是rejected ,所以,最好把 await 放在 try catch 中
async getInfo () {
try {
await this.getUserInfo()
if (this.authoritys.event_analy === 1) {
this.menuList = MENU_LIST
} else {
const checkMenuList = e => e.path !== '/analyse-event/event-list'
this.menuList = _.filter(MENU_LIST, checkMenuList)
}
} catch (error) {
} finally {
this.getSelectedMenu()
}
}
理解中出现的问题:
Promise 相比较的其他的好处: 阮一峰 讲解异步编程
事件驱动模式的理解。
引用: