JavaScript 异步编程的几个方法

2018-07-13  本文已影响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异步编程会越来越容易,功能也会越来越强大。

上一篇下一篇

猜你喜欢

热点阅读