ES6的异步操作

刚开始看书上的这一章的时候,没想到JavaScript还有异步操作,还有这种操作???果不其然,异步操作和java里面的异步操作同样,还是有点难。不过看了两三遍下来,似乎还是明白了一些。
废话不多说,那就直接进入正题吧!

重要性:JavaScript只有一个 线程,如果没有异步编程,得卡死,基本没法用。

异步

1.概念:简单来说就是不连续的执行,比如说,有一个任务分成两段,先执行第一段,转而执行其他任务,等做好准备再回过头执行第二段。
2.JavaScript语言对异步编程得实现就是回调函数,所谓回调函数,就是把任务的下一阶段单独写在一个函数中,等到重新执行该任务的时候直接调用这个函数

//读取文件进行处理是这样写的。
fs.readFile('/etc/passwd',function(err,data){
    if(err) throw err;
    console.log(data);
})
//node.js约定回调函数的第一个参数必须是错误对象err(如果没有错误,该参数就是null)
//原因是执行分成两段,在这两段之间抛出的错误程序无法捕捉,只能当做参数传入第二段

这里我有一个大胆的想法:是不是只要一个函数的第一个参数是err,都可以认为是回调函数呢?这个猜想暂时放在这里。哈哈
3.Promise的引入
其实回调函数本身没有什么问题,问题在于多个回调函数嵌套的话,比如读取第一个文件之后,再读取第二个文件,再读取第三个文件...读取第n个文件,这样下去会写很多个回调函数,gg。
因此,为了解决这个问题,引入了Promise,它不是一种新的语法功能,而是一种新的写法。例如:

var readFile = require('fs-readfile-promise');
readFile(fileA).then(function(data){
    console.log(data.toString());
}).then(function(data){
    return readFile(fileB);
}).then(function(data){
    console.log(data.toString());
}).catch(function(err){
    console.log(err);
})

上面代码存在的主要问题是代码冗余,原来的任务被Promise包装了一下,不管什么操作,一眼就能看出去都是一堆then,原来的语义变得很不清楚。
4.Generator函数的数据交换和错误处理
(1)这个函数可以暂停执行和恢复执行,这是它能封装异步任务的根本原因
(2)next方法返回值的value属性,是Generator函数向外输出数据,next方法可以接受参数,向Generator函数体内输入数据。
(3)Generator函数内部还可以部署错误处理代码,捕获函数体外抛出的错误。
5.用Generator函数执行一个真实的异步任务

var fetch = require('node-fetch');
function* gen(){
    var url = 'http://api.github.com/users/github';
    var result = yield fetch(url);
    console.log(result.bio);
}//上述代码中,Generator函数封装了一个异步操作,先读取一个远程的接口
//然后从JSON格式的数据解析信息,就像前面说的,这段代码非常像同步操作,只是加上了
//yield命令
var g = gen();
var result = g.next(); //fetch模块返回的是一个Promise对象
result.value.then(function(data){
    return data.json();
}).then(function(data){
    g.next(data);
})

6.Thunk函数
(1)编译器的传名调用实现:将一个参数放在一个临时的函数中,然后将这个临时的函数传入函数体,这个临时的函数就是Thunk函数。
(2)JavaScript语言的Thunk函数:JavaScript语言是传值调用,它的Thunk函数含义有所不同。在JavaScript语言中,Thunk函数替换的不是表达式,而是多参数函数,它将其替换成单参数的版本,且只接受回调函数作为参数。

//简单的Thunk函数转换器
var thunk = function(fn){
    return function(){
        var args = Array.prototype.slice.call(arguments);
        return function(callback){
            args.push(callback);
            return fn.apply(this,args);
        }
    }
}

//简单的Thunk函数转换器
var thunk = function(fn){
    return function(){
        var args = Array.prototype.slice.call(arguments);
        return function(callback){
            args.push(callback);
            return fn.apply(this,args);
        }
    }
}

(3)Generator函数的流程管理
以读取文件为例子
用到的函数及作用:
1.Thunk函数,这个可以通过Thunkify模块,返回一个Thunk函数(并以回调函数作为它的参数)
2.Generator函数,这个函数主要用来封装两个或者两个以上的异步操作(通过yield命令后接异步操作)
,然后通过调用这个Generator函数获取到遍历器对象,然后这个对象调用next()方法来执行yield
命令之后的异步操作,执行成功之后会返回一个对象,这个对象的value的值也就是一个只接收回调函数
的一个Thunk函数,而这个回调函数主要用next方法将执行权交回给Generator函数,像这样下去,
可以继续执行Generator函数下面的异步操作。

var fs = require('fs');
var thunkify = require('thunkify');
var readFile = thunkify(fs.readFile);

var gen = function* (){
    var r1 = yield readFile('/etc/fstab');
    console.log(r1.toString());
    var r2 = yield readFile('/etc/shells');
    console.log(r2.toString());
}

//如何手动的执行上面这个Generator函数
var g = gen();
var result = g.next();
result.value(function(err,data){
    if(err) throw err;
    var r2 = g.next(data);
    r2.value(function(err,data){
        if(err) throw err;
        g.next(data);
    })
})
//可以方法上述Generator函数的执行过程是将同一个回调函数反复传入next方法的value属性。
//其实我们可以用递归来实现这个过程。

(4)Thunk函数的自动流程管理
Thunk函数真正的魅力在于可以自动执行Generator函数。

//下面通过一个run方法来实现Generator函数的自动执行,利用的主要是Thunk函数
function run(fn){
    var gen = fn();

    function next(err,data){
        var result = gen.next(data);
        if(result.done) return;
        result.value(next);
    }
    next();
}

var fs = require('fs');
var thunkify = require('thunkify');
var readFile = thunkify(fs.readFile);

var gen = function* (){
    var r1 = yield readFile('/etc/fstab');
    console.log(r1.toString());
    var r2 = yield readFile('/etc/shells');
    console.log(r2.toString());
}
需要明白的几个点:
1.之所以能够自动执行Generator函数,是因为有Thunk函数的存在
2.Generator函数中的异步操作必须要是Thunk函数
3.在这个run方法中写这个next方法,作为Thunk函数参数的回调函数
4.在next方法中,继续将next方法传递给Thunk函数,实现递归,也就是继续自动执行

实现Generator函数自动执行的方案有:1.Thunk函数 2.Promise对象

(5)co模块:这个是一个小工具,用于Generator函数的自动执行
co模块可以让你不用编写Generator函数的执行器,Generator函数只要传入co函数就会自动执行
co函数会返回一个Promise对象,因此可以使用then方法添加回调函数。

co模块的原理

co模块其实就是一个异步操作的容器,它的自动执行只需要一种机制,当异步操作有了结果能够自动交回执行权。有两种方法:
1回调函数。将异步操作包装成Thunk函数,在回调函数中交回执行权
2.Promise对象。将异步操作包装成Promise对象,在then方法中交回执行权。

co模块的实质

将Thunk函数和Promise对象这两种自动执行机制,包装成了一个模块。使用co模块的前提条件是,Generator函数的yield命令只能是Thunk函数或者是Promise对象

var fs = require('fs');
var readFile = function(fileName){
    return new Promise(function(resolve,reject){
        fs.readFile(fileName,function(error,data){
            if(error) reject(error);
            resolve(data);
        })
    })
}

var gen = function* (){
    var f1 = yield readFile('/etc/fstab');
    var f2 = yield readFile('/etc/shells');
    console.log(f1.toString());
    console.log(f2.toString());
}

var g = gen();
g.next().value.then(function(data){
    g.next(data).value.then(function(data){
        g.next(data);
    })
})//手动执行也就是不断的添加回调函数
//下面自己写一个自动执行器
function run(gen){
    var g = gen();

    function next(data){
        var result = g.next(data);
        if(result.done) return;
        result.value.then(function(data){
            next(data);
        })
    }
    next();
}
co模块可以处理并发的异步操作

co支持并发的异步操作,即允许某些操作同时进行,等到他们全部完成才进行下一步
这时要把并发的操作都放在数组或者对象里面,跟在yield语句后面

//数组的写法
co(function* (){
    var res = yield [
        Promise.resolve(1);
        Promise.resolve(2);
    ];
    console.log(res);
}).catch(onerror);

//对象的写法
co(function* (){
    var res = yield {
        1:Promise.resolve(1);
        2:Promise.resolve(2);
    };
    console.log(res);
}).catch(onerror);

co(function* (){
    var values = [n1,n2,n3];
    yield values.map(somethingAsync);
}) //这里允许并发3个somethingAsync异步操作,等到他们全部完成才会进行下一步

function* somethingAsync(x){
    return y;
}

转载于:https://www.cnblogs.com/sminocence/p/7282458.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值