js-异步编程实现

86 阅读2分钟

js单线程

优势:任务队列简单
劣势:语句block,阻塞(表现为浏览器卡住)

由阻塞(同步执行)导致需要异步编程,代码块并不马上执行

异步操作场景

fs文件操作:

        fs.readFile('./a.txt', 'utf-8', function(err, data)=>{ 
  console.log(err? 'is Err' + err: 'data: '+ data)
        })

事件操作

    var btn = document.getElementById('btn')
    btn.addEventListener('click', e=>{
            console.log(e)
    })

网络请求

     $.ajax.get('http:127.0.0.1/users', function(data)=>{
            // 网络请求数据处理
            ...
    })

定时器

    setTimeout(()=>{
        // 延时任务处理
        ...
    })

异步解决方案

1.事件

分类:dom事件;自定义事件;
特点:点对点

    foo.on('finished', roo)
    foo(){
            setTimeout(()=>{
                    console.log('wait a little time')
                    foo.trigger('finished')
            }, 900)
    }
	
    ==>自定义事件,触发
    // 创建时间 
    let event = new customEvent('myEvent', {

    })
    // 事件监听
    document.addEventListener('myEvent', e=>{
            console.log(e)
    })
    // 事件触发 
    document.dispatchEvent(event)

2.发布订阅与观察者模式

特点:群发,广播,以信号为中心,当异步事件完成,发出相应信号,所有该消息的订阅都会被通知

手写发布订阅

思路:维护订阅列表,on、off操作列表

    class Pubsub{
            constructor() {
                    this.handlers = {}
            }
            on(eventType, handle){
                    // 异常情况抛出,类型判断
                    if(!this.handlers.hasOwnProperty(eventType)){
                            this.handlers[eventType] = []
                    }
                    if(typeof handle === 'function'){
                            this.handlers[eventType].push(handle)
                    }else{
                            console.log('no handle function')
                    }
                    return this //返回对象本身 ,以支持链式调用
            }
            emit(eventType, ...args){
                    if(this.handlers.hasOwnProperty(eventType)){
                            this.handlers[eventType].forEach((item, key, arr) =>{
                                    item.apply(null, args)
                            })
                    }else{
                            console.log('no handle func')
                    }
            }
            off(eventType, handle){
                    if(this.handlers.hasOwnProperty(eventType)){

                    }else{
                            console.log('no handle')
                    }
            }
    }
手写观察者

思路:拆分为两个角色(观察者-关注get、set、添加被观察者 ,被观察者-自身行为)

    class Subject{ //观察者
            constructor(){
                    this.state =  ''
                    this.ob = []
            }
            attach(ob){
                    this.ob.push(ob)
            }
            getState(){
                    return this.state
            }
            setState(state){
                    this.state = state
                    this.ob.forEach(ob=>{
                            ob.update()
                    })
            }
    }

    class Observer{
            constructor(name, subject){
                    this.name = name
                    this.subject = sujbect
            }
            update(){

            }
    }

3.回调方式

特点:承前启后,耦合、回调地狱

    function roo(){
            console.log('run roor')
    }
    foo(callback){
            setTimeout(()=>{
                    console.log('wait a little time')
                    callback()
            }, 900)
    }
    foo(roo)		

4.promise方式

    then的方式实现链式调用,操作执行顺序
    asyncawait

5.手写的步进器

特点:通过闭包实现

    function takeStep(arr){
            let index = 0
            return  {
                    next: function(){
                            return (
                                    arr.length > index?{
                                            value: arr[index++]
                                    }: {
                                            done: true
                                    }
                            )
                    }
            }
    }

6.迭代器 方式的步进器

    const iteatorer = arr => {
            [Symbol.iterator](){
                    let index = 0
                    return  {
                            next: function(){
                                    return (
                                            arr.length > index?{
                                                    value: arr[index++]
                                            }: {
                                                    done: true
                                            }
                                    )
                            },
                            return(){
                                    return {
                                            done: true
                                    }
                            }
                    }
            }
    }
    for(let   value of iteatorer([1,2,3,4])){
            console.log(value)
            if(value > 1){
                    break;
            }
    }

    // generator-es6的步进
    function *generator(){
            yield 1;
            yield 2;
    }
    let  generator = generator()
    generator.next() // {value: 1, done: false}
    generator.next() // {value: 2, done: false}
    ganerator.next() // {value: undefined, done: true}