• (翻译)异步编程之Promise(1):初见魅力


    原文:https://www.promisejs.org/

    by Forbes Lindesay

    异步编程系列教程:

    1. (翻译)异步编程之Promise(1)——初见魅力
    2. 异步编程之Promise(2):探究原理
    3. 异步编程之Promise(3):拓展进阶
    4. 异步编程之Generator(1)——领略魅力
    5. 异步编程之Generator(2)——剖析特性
    6. 异步编程之co——源码分析

    动机


    思考一下,下面这段用来读取文件并解析JSON的Javascript同步代码。它很简单并且易于阅读,但是因为它会阻塞代码,你并不会想用在大多数的应用里。这意味着,当你用它来读取文件的时候(它需要很多时间)不会有其他的事情发生。

    function readJSONSync(filename){
    	// 读取文件后,再解析成JSON
        return JSON.parse(fs.readFileSync(filename, 'utf-8'));
    }
    

    为了让我们的应用高性能且实时响应,我们需要让所有涉及到IO的操作都变成异步的。最简单的方法去实现它就是使用callback回调。然而,一个幼稚不成熟的代码实现也许会让它出错。

    function readJSON(filename, callback){
        fs.readFile(filename, 'utf-8', function(err, res){
            if(err)
                return callback(err);
            // 回调参数为,错误null和文件解析后的JSON
            callback && callback(null, JSON.parse(res));
        })
    }
    

    出现的问题:

    • 额外的callback回调参数会使我们困惑,不知道变量到底是输入值还是返回值。
    • It doesn't work at all with control flow primitives.(这里我无法理解控制什么流呢?)
    • 无法处理由JSON.parse抛出的错误。

    我们需要处理由JSON.parse抛出的错误,但是我们同样也需要小心不要影响到了由callback函数抛出的错误。最后我们用一堆混乱的错误处理完成了:

    function readJSON(filename, callback){
    	fs.readFile(filename, 'utf-8', function(err, res){
    		if(err)
    			return callback(err);
    		try{
    			res = JSON.parse(res);
    		} catch(ex){
    			return callback(ex);
    		}
    		callback(null, res);
    	});
    }
    

    尽管有这些杂乱代码来处理错误,我们仍然留下一个问题就是callback烦人的回调参数。Promise可以帮助你更自然的处理错误,没有callback的参数使代码更简洁,并且用不着修改底层的结构(意思是你可以用原生Js来实现promise,并且用它来封装已经存在的异步操作)

    什么是promise?


    promise背后的核心思想就是,一个promise代表了一个异步操作的结果。一个promise只有三种不同的形态:

    • pending - 等待,是promise的初始状态
    • fulfilled - 完成,这个promise状态代表着操作成功(有的也称resolve解决)
    • rejected - 拒绝,这个promise状态代表了操作失败

    一旦promise是fulfilled状态或rejected状态,那么它就是固定不会再改变的了。

    构建一个promise


    当以后所有的APIs都转变成promises,你应该会特别少机会去手动构建promise。在此期间,我们需要一个方法来转变现有的APIs。举个栗子:

    function readFile(filename, encoding){
    	return new Promise(function(resolve, reject){
    		fs.readFile(filename, encoding, function(err, res){
    			if(err)
    				return reject(err);
    			resolve(res);
    		});
    	});
    }
    

    我们使用ES6的new Promise来构建promise。我们给构造器一个生成promise的工厂函数。这个带两个参数的函数会立即调用。第一个参数用来使promise转变成成功状态,第二个参数使promise转变成失败状态。一旦操作完成后,我们将会调用相应合适的函数。

    等待一个promise


    为了使用promise,我们必须用某种方法去等待promise的状态是成功了还是失败了。这个方法在promise/A里,就是使用promise.then(resolve, reject)promise.then()会返回promise以提供链式调用。

    根据这个,我们可以利用promise轻松的重写之前的readJSON函数:

    function readJSON(filename, encoding){
    	return new Promise(function(resolve, reject){
    		readFile(filename, encoding).then(function(res){
    			try{
    				resolve(JSON.parse(res));
    			}catch(ex){
    				reject(ex);
    			}
    		}, reject);
    	});
    }
    

    这次,我们是把readJSON转化成新的promise返回出来,提供接下来的使用。

    变化/链式结构


    通过我们的例子,我们真正希望做到的是让另外的操作也变成promise化。在我们的例子中,第二个操作是同步的(指JSON.parse()),但是readJSON已经简单的转变成一个异步的操作。幸运的是,promise的then()方法可以将其变成链式操作。

    现在我们可以更简洁地重写我们原本的例子:

    function readJSON(filename, encoding){
    	return readFile(filename, encoding).then(function(res){
    		return JSON.parse(res);
    	});
    }
    

    因为JSON.parse仅仅是个函数,我们可以重写成这样:

    function readJSON(filename, encoding){
    	return readFile(filename, encoding).then(JSON.parse);
    }
    

    这和我们一开始写的最简单的同步代码已经非常相似了!我认为用链式结构调用,会更符合自然逻辑。

    最后实现的代码如下所示:

    //data.json文件
    //{"message": "Hello World!"}
    
    //readFile的Promise化
    function readFile(filename, encoding){
        return new Promise(function(resolve, reject){
            fs.readFile(filename, encoding, function(err, res){
                if(err)
                    return reject(err);
                resolve(res);
            });
        });
    }
    
    //readJSON的Promise化
    function readJSON(filename, encoding){
        return readFile(filename, encoding).then(JSON.parse);
    }
    
    // readJSON函数的使用
    readJSON("data.json", 'utf-8').then(function(data){
        console.log(data.message); // Hello World!
    });
    
    

    我们可以看到臃肿混乱的回调金字塔已经消失了,剩下的是清爽干净的链式promise。而错误,我们也可以很轻松的进行捕捉处理。

  • 相关阅读:
    导入导出
    封装本地文件路径
    读书书单
    Spring源码阅读-BeanFactory体系结构分析
    Spring源码阅读-ApplicationContext体系结构分析
    Spring源码阅读-IoC容器解析
    Spring源码阅读环境搭建
    【spring实战第五版遇到的坑】第14章spring.cloud.config.uri和token配置项无效
    【spring实战第五版遇到的坑】4.2.3中LDAP内嵌服务器不启动的问题
    【spring实战第五版遇到的坑】3.2中配置关系映射时,表名和3.1中不一样
  • 原文地址:https://www.cnblogs.com/YikaJ/p/4468325.html
Copyright © 2020-2023  润新知