美文网首页
ES6 Promise(读书笔记)

ES6 Promise(读书笔记)

作者: 他在写的 | 来源:发表于2018-12-30 11:56 被阅读0次

    一下内容摘自http://liubin.org/promises-book/#ch2-promise-race,JavaScript Promise迷你书(中文版)

    1.什么是promise

    Promise是抽象异步处理对象以及对其进行各种操作的组件。 其详细内容在接下来我们还会进行介绍,Promise并不是从JavaScript中发祥的概念。

    Promise最初被提出是在 E语言中, 它是基于并列/并行处理设计的一种编程语言。

    现在JavaScript也拥有了这种特性,这就是JavaScript Promise。

    说到基于JavaScript的异步处理,首先都会想到利用回调函数。

    getAsync("fileA.txt", function(error, result){
        if(error){// 取得失败时的处理
            throw error;
        }
        // 取得成功时的处理
    });
    

    Node.js等则规定在JavaScript的回调函数的第一个参数为 Error 对象,这也是它的一个惯例。

    像上面这样基于回调函数的异步处理如果统一参数使用规则的话,写法也会很明了。 但是,这也仅是编码规约而已,即使采用不同的写法也不会出错。

    而Promise则是把类似的异步处理对象和处理规则进行规范化, 并按照采用统一的接口来编写,而采取规定方法之外的写法都会出错。

    1.2 创建一个Promise对象

    创建promise对象的流程如下所示。

    1.new Promise(fn) 返回一个promise对象

    2.在fn 中指定异步等处理

    -处理结果正常的话,调用resolve(处理结果值)
    
    -处理结果错误的话,调用reject(Error对象)
    
    var promise = getAsyncPromise("fileA.txt"); 
    promise.then(function(result){
        // 获取文件内容成功时的处理
    }).catch(function(error){
        // 获取文件内容失败时的处理
    });
    

    我们可以向这个预设了抽象化异步处理的promise对象, 注册这个promise对象执行成功时和失败时相应的回调函数。

    这和回调函数方式相比有哪些不同之处呢? 在使用promise进行一步处理的时候,我们必须按照接口规定的方法编写处理代码。

    也就是说,除promise对象规定的方法(这里的 then 或 catch)以外的方法都是不可以使用的, 而不会像回调函数方式那样可以自己自由的定义回调函数的参数,而必须严格遵守固定、统一的编程方式来编写代码。

    这样,基于Promise的统一接口的做法, 就可以形成基于接口的各种各样的异步处理模式。

    所以,promise的功能是可以将复杂的异步处理轻松地进行模式化, 这也可以说得上是使用promise的理由之一。

    2. Promise的方法链

    promise可以写成方法链的形式

    aPromise.then(function taskA(value){
        // task A
        }).then(function taskB(vaue){
        // task B
        }).catch(function onRejected(error){
            console.log(error);
        });
    

    如果把在 then 中注册的每个回调函数称为task的话,那么我们就可以通过Promise方法链方式来编写能以taskA → task B 这种流程进行处理的逻辑了。

    如果我们将方法链的长度变得更长的话,那在每个promise对象中注册的onFulfilled和onRejected将会怎样执行呢?

    看下面的promise chain:
    ···
    function taskA() {
    console.log("Task A");
    }
    function taskB() {
    console.log("Task B");
    }
    function onRejected(error) {
    console.log("Catch Error: A or B", error);
    }
    function finalTask() {
    console.log("Final Task");
    }

    var promise = Promise.resolve();
    promise
        .then(taskA)
        .then(taskB)
        .catch(onRejected)
        .then(finalTask);
    

    ···

    上面代码中的promise chain的执行流程,一张图描述就是这样:


    promise-then-catch-flow.png

    如果Task A 处理中发生异常的话,会按照TaskA → onRejected → FinalTask 这个流程来进行处理。(TaskB不会被调用)

    2.1 Promise chain 如何传递参数

    前面例子中的Task都是相互独立的,只是被简单调用而已。

    这时候如果 Task A 想给 Task B 传递一个参数该怎么办呢?

    答案非常简单,那就是在 Task A 中 return 的返回值,会在 Task B 执行时传给它。

    我们还是先来看一个具体例子吧。

    function doubleUp(value) {
        return value * 2;
    }
    function increment(value) {
        return value + 1;
    }
    function output(value) {
        console.log(value);// => (1 + 1) * 2
    }
    
    var promise = Promise.resolve(1);
    promise
        .then(increment)
        .then(doubleUp)
        .then(output)
        .catch(function(error){
            // promise chain中出现异常的时候会被调用
            console.error(error);
        });
    

    这段代码的入口函数是 Promise.resolve(1); ,整体的promise chain执行流程如下所示:

    1.Promise.resolve(1); 传递 1 给 increment 函数

    2.函数 increment 对接收的参数进行 +1 操作并返回(通过return)

    3.这时参数变为2,并再次传给 doubleUp 函数

    4.最后在函数 output 中打印结果

    每个方法中 return 的值不仅只局限于字符串或者数值类型,也可以是对象或者promise对象等复杂类型。

    return的值会由 Promise.resolve(return的返回值); 进行相应的包装处理,因此不管回调函数中会返回一个什么样的值,最终 then 的结果都是返回一个新创建的promise对象。

    2.2 每次调用then都会返回一个新创建的promise对象

    从代码上乍一看, aPromise.then(...).catch(...) 像是针对最初的 aPromise 对象进行了一连串的方法链调用。

    然而实际上不管是 then 还是 catch 方法调用,都返回了一个新的promise对象。

    下面我们就来看看如何确认这两个方法返回的到底是不是新的promise对象。

    var aPromise = new Promise(function (resolve) {
        resolve(100);
    });
    var thenPromise = aPromise.then(function (value) {
        console.log(value);
    });
    var catchPromise = thenPromise.catch(function (error) {
        console.error(error);
    });
    console.log(aPromise !== thenPromise); // => true
    console.log(thenPromise !== catchPromise);// => true
    

    === 是严格相等比较运算符,我们可以看出这三个对象都是互不相同的,这也就证明了 then 和 catch 都返回了和调用者不同的promise对象。

    如果我们知道了 then 方法每次都会创建并返回一个新的promise对象的话,那么我们就应该不难理解下面代码中对 then 的使用方式上的差别了。

    // 1: 对同一个promise对象同时调用 `then` 方法
    var aPromise = new Promise(function (resolve) {
        resolve(100);
    });
    aPromise.then(function (value) {
        return value * 2;
    });
    aPromise.then(function (value) {
        return value * 2;
    });
    aPromise.then(function (value) {
        console.log("1: " + value); // => 100
    })
    
    // vs
    
    // 2: 对 `then` 进行 promise chain 方式进行调用
    var bPromise = new Promise(function (resolve) {
        resolve(100);
    });
    bPromise.then(function (value) {
        return value * 2;
    }).then(function (value) {
        return value * 2;
    }).then(function (value) {
        console.log("2: " + value); // => 100 * 2 * 2
    });
    

    第1种写法中并没有使用promise的方法链方式,这在Promise中是应该极力避免的写法。这种写法中的 then 调用几乎是在同时开始执行的,而且传给每个 then 方法的 value 值都是 100 。

    第2中写法则采用了方法链的方式将多个 then 方法调用串连在了一起,各函数也会严格按照 resolve → then → then → then 的顺序执行,并且传给每个 then 方法的 value 的值都是前一个promise对象通过 return 返回的值。

    2.3 Promise.all

    Promise.all方法接受一个promise对象的数组作为参数,当这个数组里的所有promise对象全部变为resolve或reject状态的时候,它才会去调用.then方法。

    下面例子中, getURL 返回了一个promise对象,它封装了XHR通信的实现。 向 Promise.all 传递一个由封装了XHR通信的promise对象数组的话,则只有在全部的XHR通信完成之后(变为FulFilled或Rejected状态)之后,才会调用 .then 方法。

    function getURL(URL) {
        return new Promise(function (resolve, reject) {
            var req = new XMLHttpRequest();
            req.open('GET', URL, true);
            req.onload = function () {
                if (req.status === 200) {
                    resolve(req.responseText);
                } else {
                    reject(new Error(req.statusText));
                }
            };
            req.onerror = function () {
                reject(new Error(req.statusText));
            };
            req.send();
        });
    }
    var request = {
            comment: function getComment() {
                return getURL('http://azu.github.io/promises-book/json/comment.json').then(JSON.parse);
            },
            people: function getPeople() {
                return getURL('http://azu.github.io/promises-book/json/people.json').then(JSON.parse);
            }
        };
    function main() {
        return Promise.all([request.comment(), request.people()]);
    }
    // 运行示例
    main().then(function (value) {
        console.log(value);
    }).catch(function(error){
        console.log(error);
    });
    

    在上面的代码中,request.comment()request.people() 会同时开始执行,而且每个promise的结果(resolve或reject时传递的参数值),和传递给 [Promise.all]的promise数组的顺序是一致的。

    也就是说,这时候 .then 得到的promise数组的执行结果的顺序是固定的,即 [comment, people]。

    2.4 Promise.race

    它的使用方法和Promise.all一样,接收一个promise对象数组为参数。

    Promise.all 在接收到的所有的对象promise都变为 FulFilled 或者 Rejected 状态之后才会继续进行后面的处理, 与之相对的是 Promise.race 只要有一个promise对象进入 FulFilled 或者 Rejected 状态的话,就会继续进行后面的处理。

    相关文章

      网友评论

          本文标题:ES6 Promise(读书笔记)

          本文链接:https://www.haomeiwen.com/subject/ehcvlqtx.html