JS-同步异步理解以及 async 和 await

1,949 阅读5分钟

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 相比较的其他的好处: 阮一峰 讲解异步编程

事件驱动模式的理解。

引用:

阮一峰 ES6教程

阮一峰 JS异步编程的方法