• 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;
    }
    
  • 相关阅读:
    显示和隐藏一个div的问题
    javascript高级程序设计笔记-第八章(BOM)
    javascript高级程序设计笔记-第七章(函数表达式)
    javascript高级程序设计笔记-第六章(面向对象编程)
    javascript高级程序设计笔记-第五章(引用类型)
    javascript高级程序设计笔记-第四章(变量、作用域)
    javascript高级程序设计笔记-第三章(基本概念)
    css设计指南-笔记7
    CocoaPods 1.1.0上传遇到swift问题
    Attribute富文本使用方法
  • 原文地址:https://www.cnblogs.com/sminocence/p/7282458.html
Copyright © 2020-2023  润新知