美文网首页
JavaScript 异步编程的几个方法

JavaScript 异步编程的几个方法

作者: 海是那片海 | 来源:发表于2018-07-13 10:36 被阅读0次

    原文地址
    基于浏览器事件轮回机制(以及nodejs中的事件轮询机制),JavaScript常常会运行在异步环境中。而JavaScript是单线程语言,使用对js的异步操作是不可避免的。
    解决方法有以下几点:

    1,回调

    回调是比较原始的方法
    ep:

    // fs => node的文件系统
    const fs = require('fs');
    const readFileAsArray = function(file,cb){
        fs.readFile(file,(err,data)=>{
            if(err) return cb(err);
            // \n => 回车键
            const lines = data.toString().trim().split('\n');
            cb(null,lines);
        })
    }
    readFileAsArray('./numbers.txt',(err,lines)=>{
        if(err) throw err;
        const numbers = lines.map(Number);
        console.log(`分别抢到了${numbers}块红包`)
    })
    // 分别抢到了1,2,3,4,5,6,7,9,10,11,12,13块红包
    

    上面的例子,通过readFileAsArray()函数的结果,回调readFileAsArray()函数。
    这个例子充分说明了,当代码逻辑复杂或操作步骤多的时候,会写很多层,很难阅读和维护(即使是自己写的,也很难快速上手)。

    2,Promise

    像上面的回调,他的执行顺序是控制在代码手里(你没办法去手动调整执行顺序---必须干完才干一件事,没有一个显示化的流程),而promise让控制权回到的人的手里,流程更干净,可视化(通过一系列方法---then/catch/all/race等控制异步流程)。

    const fs = require('fs');
    const readFileAsArray = function(file){
        return new Promise((resolve,reject)=>{
            fs.readFile(file,(err,data)=>{
                if(err){
                    reject(err);
                }
                const lines = data.toString().split('\n');
                resolve(lines);
            })
        })
    }
    readFileAsArray('./numbers.txt').then(
        lines=>{
            const numbers = lines.map(Number);
            console.log(`${numbers}`)
        }
    ).catch(error=>{
        console.log(error)
    })
    

    但Promise也有单值/不可取等缺点

    3,await/async

    await/async是ES7推出的语法糖,内部封装了Promise和Generator的组合使用方式。(程序员懒--PromiseAPI太多---await/async就出现了)

    const fs = require('fs');
    const readFileAsArray = function(file){
        return new Promise((resolve,reject)=>{
            fs.readFile(file,(err,data)=>{
                if(err){
                    reject(err);
                }
                const lines = data.toString().split('\n');
                resolve(lines);
            })
        })
    }
    async function result(){
        try{
            const lines = await readFileAsArray('./numbers.txt');
            const numbers = lines.map(Number);
            console.log(`${numbers}`)
        } catch (err){
            console.log("await 出错!");
            console.log(err);
        }
    }
    result();
    

    4,event

    回调(promise、await/async)和event的关系就像计划经济和市场经济,一个人为控制,一个根据需求和供给控制。

    // 当``EventEmitter``对象触发一个事件时,所绑定在该事件上的函数都被同步调用,监听器的返回值会被丢弃。
    // eventEmitter.on => 用于注册监听器
    // eventEmitter.emit() =>用于触发改事件
    const EventEmitter = require('events');
    const fs = require('fs');
    class MyEventEmitter extends EventEmitter {
        executeAsy(asyncFunc,args){
            this.emit('begin');
            console.time('执行耗时');
            asyncFunc(args,(err,data)=>{
                if(err) return this.emit('err',err);
                this.emit('data',data);
                console.timeEnd('执行耗时');
                this.emit('结束')
            })
        }
    }
    const myEventEmitter = new MyEventEmitter();
    myEventEmitter.on('开始',()=>{
        console.log('开始执行了');
    })
    myEventEmitter.on('data',data=>{
        console.log(`${data}`)
    })
    myEventEmitter.on('结束',()=>{
        console.log('结束执行了')
    })
    myEventEmitter.on('err',err=>{
        console.log(err)
    })
    myEventEmitter.executeAsy(fs.readFile,'./numbers.txt')
    

    event => 代码量太多

    5,rxjs

    rxjs和异步的关系:它可以把数据转化成一股流,无论是数据同步得到的还是异步得到的,是单值还是多值。
    Rx.Observable.of用来包装单值同步数据,
    Rx.Observable.fromPromise 用来包装单值异步数据
    Rx.Observable.fromEvent用来包装多值异步数据

    // import { Observable } from 'rxjs'
    const fs = require('fs');
    const Rx = require('rxjs');
    const Observable = require('rxjs')
    const EventEmitter = require('events');
    class MyEventEmitter extends EventEmitter{
        async executeAsy(asyncFunc,args){
            this.emit("开始");
            try{
                console.time('执行耗时');
                const data = await asyncFunc(args);
                this.emit('data',data);
                console.timeEnd('执行耗时');
                this.emit('结束');
            } catch(err){
                console.log('出错了!')
                this.emit('error',err)
            }
        }
    }
    const readFileAsArray = function(file){
        return new Promise((resolve,reject)=>{
            fs.readFile(file,(err,data)=>{
                if(err){
                    reject(err);
                }
                const lines = data.toString().split('\r\n');
                resolve(lines);
            })
        })
    }
    const myEventEmitter = new MyEventEmitter();
    myEventEmitter.executeAsy(readFileAsArray,'./numbers.txt');
    let dataObservable = Observable.fromEvent(myEventEmitter,'data');
    let subscription = dataObservable.subscribe(data=>{
        console.log(`${data}`)
    },err=>{
        console.error(err);
    },compelete=>{
        console.info('compelete!');
    })
    

    rxjs还有很多重要的概念,比如生产者Observe和消费者Observable、推拉模型、各种方便的操作符和函数式编程等。
    ES8已经着手Observable和Observe的实现,node也在着手异步生命周期钩子Async Hooks来方便程序员调试异步程序,未来的js异步编程会越来越容易,功能也会越来越强大。

    相关文章

      网友评论

          本文标题:JavaScript 异步编程的几个方法

          本文链接:https://www.haomeiwen.com/subject/bajfpftx.html