异步编程

135 阅读5分钟
  • 事件监听
  • 回调函数
  • 发布-订阅
  • promise函数
  • genarate函数

事件监听

事件理解为js可以监听到的行为,网页中的每一个元素都可以触发一些js的事件 常见的比如click,mouseover...等等 任务的执行不取决于代码的顺序,而取决于某个事件是否发生。

事件流

事件流主要分为3个阶段

  • 捕获阶段
  • 目标阶段
  • 冒泡阶段

冒泡事件流和捕获事件流恰恰想反 现在的标准是捕获+冒泡,所有的事件都是从顶部的document对象开始到document对象结束 例:

<html>
    <body>
        <div> Click </div>
    </body>
</html>

事件整体流向是由    document=>html=>body=>div=>body=>html=>document 在开发中我们大部分情况下都是关注的冒泡阶段,

事件委托

当我们需要给多个子节点绑定绑定事件时,可以不必给每一个子元素都绑定一次事件,这样过于繁琐,基于事件冒泡原理,我们只要给父元素绑定相同事件即可,在事件处理函数中拿到由系统自动创建的事件对象,通过eventtarget拿到当前事件的状态,通过状态中的具体信息来分辨是哪一个子元素,从而进行相应的处理

事件绑定

一般我们使用on和addEventListener 前者同一类型事件只能绑定一个事件处理函数,后者可以绑定多个 后者还可以通过设置第三个参数的值为true or false 来设置该事件是不是在捕获阶段执行,默认为false

回调函数

回调函数也被称为高阶函数,是一个被作为参数传递给另一个函数的函数,回调函数的执行需要等待外部的函数执行返回结果
这是异步的最基本方法 较为常见的有

    $.ajax({
            url:"http://fiddle.jshell.net/favicon.png",
            success:successCallback,
            complete:completeCallback,
            error:errorCallback
        });

一个比较经典的例子:
    你到一个商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货。在这个例子里,你的电话号码就叫回调函数,你把电话留给店员就叫登记回调函数,店里后来有货了叫做触发了回调关联的事件,店员给你打电话叫做调用回调函数,你到店里去取货叫做响应回调事件

发布订阅

发布订阅中的事件, 可以理解为信号,还是上面的便利店的例子,信号就是店员打电话通知你有货了,然后你去店里取货也就是开始执行回调函数即去商店买你需要的东西
具体使用,例如 pubsub

PubSub.publish('delete', data) //发布消息
PubSub.subscribe('delete', function(data){}); //订阅
// "delete":消息名,data:发布的消息

Promise

使用Promise来将异步任务转换成同步代码执行,主要是因为promise的每一个异步任务都会返回一个promise对象,该对象有一个then方法,用来指定回调函数 例如fn1的回调函数是fn2,那么可以写成fn1().then(fn2),也叫作链式调用,可以很清楚的看到程序执行的流程

也可以使用Promise的语法糖async和await,使用async修饰方法,那么该方法就是一个异步方法,被修饰的方法返回值是一个promise对象,如果方法中有抛出错误也可以使用方法返回值,即promise对象的.catch(()=>)来对错误进行处理,await,字面意思就是等待,只能用在async修饰的方法中,在方法中执行的异步方法前使用await等待,可以保证被等待的动作执行完毕后再执行后续代码

Generator函数

function* gen(x){
  var y = yield x + 2;
  return y;
}

上方就一个Generator函数,和普通函数的区别就在于,它是可以暂停执行的,要暂停的地方,都用 yield 语句注明。

var g = gen(1);
g.next() // { value: 3, done: false }
g.next() // { value: undefined, done: true }
function* generator() {
    yield 'status 1'   // yield 表达式是暂停执行的标记  
    yield 'status 2' 
    yield 'status 3'
    yield 'status 4' 
    yield 'status 5'
    yield 'status 6'
    return 'hello world'
  }

函数关键字和方法名中间有一个*, 这个星号写在中间就行,不管是不是靠近关键字或者方法名,或者是用空格将两者隔开,不过推荐靠近函数关键字

函数体内使用yield表达式,定义各种不同的状态,

generate的使用

let iterator = generator() //调用 Generator函数,函数并没有执行,返回的是一个Iterator对象
iterator.next()              // {value: "status 1", done: false},value 表示返回值,done 表示遍历还没有结束
function* gen() {
      let result = yield 3 + 5 + 6
      console.log(result)
      yield result
    }
  
    let it = gen()
    console.log(it.next())      // it.next()的返回值中的value 是yield表达式的返回值   {value: 14, done: false}
    console.log(it.next(3))      // 3    {value: 3, done: false}
    console.log(it.next());     //{value: undefined , done: true}
  
    /* 
    执行顺序
      
      let result = yield 3 + 5 + 6
      执行顺序是先声明也就是先执行表达式左边进行声明
      let result = undefined
      再执行右边,遇到yidle表达式,返回表达式的值并暂停,即返回14,
      有返回值的情况下next返回的对象中的value就是yidle的值
      由于暂停,14无法给result赋值

      调用next(3)
      紧接着上一个yield的下一行开始执行,
      即:给result赋值
      将next的参数传给result
      
      打印result  //3
      遇到yield 表达式,返回表达式的值并暂停 
      打印返回值  :{value: 3, done: false}

      调用 next()
      函数执行完成,没写return语句,默认返回undefined,
      遇到return语句,即所有状态遍历完毕,done的值为true
      打印返回值{value: undefined, done: true}

      如果再继续调用next方法,因为函数已经执行完毕,没有其他状态了 ,所以返回的是{value: undefined, done: true}
      后续就算继续调用next方法还是这个返回值

    */