zoukankan      html  css  js  c++  java
  • (翻译)异步编程之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。而错误,我们也可以很轻松的进行捕捉处理。

  • 相关阅读:
    防删没什么意思啊,直接写废你~
    绝大多数情况下,没有解决不了的问题,只有因为平时缺少练习而惧怕问题的复杂度,畏惧的心理让我们选择避让,采取并不那么好的方案去解决问题
    Java 模拟面试题
    Crossthread operation not valid: Control 'progressBar1' accessed from a thread other than the thread it was created on
    一步步从数据库备份恢复SharePoint Portal Server 2003
    【转】理解 JavaScript 闭包
    Just For Fun
    The database schema is too old to perform this operation in this SharePoint cluster. Please upgrade the database and...
    Hello World!
    使用filter筛选刚体碰撞
  • 原文地址:https://www.cnblogs.com/YikaJ/p/4468325.html
Copyright © 2011-2022 走看看