zoukankan      html  css  js  c++  java
  • 第6章 未来的函数:生成器和promise

    1. 生成器函数

    1.1 定义生成器函数

    // 在关键字function后面添加 * 定义生成器函数
    function* newGenerator() {
        // ...
        // 在生成器内部使用yield生成独立的值
        yield "One";
        yield "Two";
        // ...
    }
    // 调用生成器函数会创建一个迭代器(iterator)对象
    let result = newGenerator();
    console.log(typeof result === "object");
    // true
    

    1.2 迭代器对象

     function* newGenerator() {
        // ...
        yield "One";
        yield "Two";
        // ...
    }
    // 调用生成器函数创建迭代器函数
    const result = newGenerator();
    console.log(typeof result === "object");
    // true
    
    // 显式调用迭代器中的next方法依次取出迭代器中的值
    const r1 = result.next();
    console.log(r1);
    // {value: "One", done: false}
    // next方法返回的对象包含两个属性:
    // value为函数的返回值,done表示迭代器函数是否已经完成
    
    const r2 = result.next();
    console.log(r2);
    // {value: "Two", done: false}
    
    const r3 = result.next();
    console.log(r3);
    // {value: undefined, done: true}
    // 生成器的值全部返回后,value的值为undefined,done为true
    

    1.3 对迭代器进行迭代

    function* newGenerator() {
        // ...
        yield "One";
        yield "Two";
        // ...
    }
    
    const result = newGenerator();
    let item;       // 创建变量保存生成器产生的单个值
    // 通过done属性判断生成器是否完成
    while(!(item = result.next()).done) {
        console.log(item.value);
        // One
        // Two
    }
    

    上述while循环是for-of循环的实现原理。for-of只是对迭代器进行迭代的语法糖

    function* newGenerator() {
        // ...
        yield "One";
        yield "Two";
        // ...
    }
    
    for(let value of newGenerator()) {
        console.log(value);
        // One
        // Two
    }
    

    1.4 把执行权交给下一个生成器

    function* newGenerator() {
        yield "One";
        // yield* 将执行权交给了另一个生成器
        // 类似于在普通函数中调用另外一个函数
        yield* anotherGenerator();
        yield "Two";
    }
    
    function* anotherGenerator() {
        yield "Wango";
        yield "Lily";
    }
    
    const STRING = [];
    for(let value of newGenerator()) {
        STRING.push(value);
    }
    
    console.log(STRING);
    // ["One", "Wango", "Lily", "Two"]
    

    2. 使用生成器

    2.1 用生成器生成ID

    function* idGenerator() {
        let id = 0;
        while(true) {
            yield ++id;
        }
    }
    
    const idIterator = idGenerator();
    
    const per1 = {id: idIterator.next().value};
    const per2 = {id: idIterator.next().value};
    const per3 = {id: idIterator.next().value};
    
    console.log(per1.id);       // 1
    console.log(per2.id);       // 2
    console.log(per3.id);       // 3
    

    2.2 用迭代器遍历DOM树

    <div id="subTree">
        <form>
            <input type="text">
        </form>
        <p>Paragraph</p>
        <span>Span</span>
    </div>
    <script>
        // 递归遍历DOM
        function traverseDOM(elem, callback) {
            callback(elem);
            elem = elem.firstElementChild;
            while(elem) {
                traverseDOM(elem, callback);
                elem = elem.nextElementSibling;
            }
        }
    
        const subTree = document.getElementById("subTree");
        traverseDOM(subTree, function(elem){
            console.log(elem.nodeName);
        });
    
        // 生成器遍历DOM,以迭代的方式书写概念上递归的代码
        function* DomTraversal(elem) {
            yield elem;
            elem = elem.firstElementChild;
            while(elem) {
                yield* DomTraversal(elem);
                elem = elem.nextElementSibling;
            }
        }
    
        const subTree = document.getElementById("subTree");
        for(let elem of DomTraversal(subTree)) {
            console.log(elem.nodeName);
        }
    </script>
    

    3. 与生成器交互

    3.1 用参数和next方法发送值

    // 以下代码仅作演示用,不具备任何实际意义
    
    // 生成器函数也可以接收参数
    function* NumGenerator(num) {
        const NUM = ["零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"];
        while(true) {
            // yield向外部返回一个对象的同时,可以从next方法接收一个新值
            num = yield NUM[num];
        }
    }
    
    const numIterator = NumGenerator(1);
    
    const num1 = numIterator.next();
    const num2 = numIterator.next(5);
    const num3 = numIterator.next(9);
    
    console.log(num1.value);        // 壹
    console.log(num2.value);        // 伍
    console.log(num3.value);        // 玖
    

    next方法为等待中的yield表达式提供了值,所以,如果没有等待中的yield表达式,也就没有什么值能应用的。基于这个原因,我们无法通过第一次调用next方法来向生成器提供该值。但如果需要为生成器提供一个初始值,可以调用生成器自身,就像上述中的const numIterator = NumGenerator(1);

    3.2向生成器抛出异常以传递值

    function* NewGenerator() {
        try {
            yield "First call";
        }
        catch (e) {
            console.log("接收值:" + e);
        }
    }
    
    const newIterator = NewGenerator();
    
    const num1 = newIterator.next();
    console.log(num1.value);
    // First call
    
    // 通过在所有迭代器上都有效的throw方法,向生成器抛出异常并夹带一个值
    const num2 = newIterator.throw("Wango");
    // 接收值:Wango
    

    4. promise

    • promise对象是对我们现在尚未得到但将来会得到值的占位符;它是对我们最终能够得知异步计算结果的一种保证。如果我们兑现了我们的承诺,那结果会得到一个值。如果发生了问题,结果则是一个错误,一个为什么不能交付的借口。使用promise的最佳案例是从服务器获取数据。

    4.1 创建一个简单的promise

    // 通过内置Promise构造函数创建promise对象
    // 构造函数接收一个函数(即执行函数),这个函数接收两个函数参数(均为内置函数)
    const dataPromise = new Promise((resolve, reject) => {
        // 这部分代码会被立即执行
        
        // 调用resolve函数,传入最终数据,表示一个promise将被成功兑现
        resolve("Wango");
    
        // 调用reject则promise被拒绝
        // reject("An error");
    });
    
    // then方法接收两个回调函数参数,promise成功兑现后会调用第一个回调函数
    // 出现错误则调用第二个回调函数
    // 这两个回调函数总是会异步调用
    dataPromise.then(data => {
        // 回调函数参数data是resolve传递过来的最终数据
        console.log(data);
        // Wango
    }, err => {
        // err是reject传递过来的数据
        console.log(err);
    });
    

    4.2 简单回调函数所带来的问题

    • 回调函数发生错误时,无法用内置语言结构来处理,导致错误经常丢失
    • 回调函数执行连续步骤非常棘手,需要嵌套一堆回调函数
    • 回调函数执行很多并行任务也很棘手,需要书写很多样板代码用于并行执行多个任务

    4.3 深入promise

    promise的执行顺序

    const dataPromise = new Promise((resolve, reject) => {
        console.log("Processing dataPromise");
        setTimeout(() => {
            resolve("dataPromise resolved");
        }, 1000);
    });
    
    dataPromise.then(data => {
        console.log(data);
    }, err => {
        console.log(err);
    });
    
    const anotherPromise = new Promise((resolve, reject) => {
        console.log("Processing anotherPromise");
        resolve("anotherPromise resolved");
    });
    
    anotherPromise.then(data => {
        console.log(data);
    });
    console.log("At cold end");
    // Processing dataPromise
    // Processing anotherPromise
    // At cold end
    // anotherPromise resolved
    // dataPromise resolved
    

    Promise构造函数在定义之后按先后顺心立即执行,而then方法会在promise兑现成功(或失败)后按完成时间先后执行(异步)

    4.4 拒绝promise

    • 显式拒绝(调用reject方法)
    const dataPromise = new Promise((resolve, reject) => {
        // 显式拒绝
        reject("An error");
    });
    
    dataPromise.then(
        data => console.log(data),
        err => console.log(err)     // An error
    );
    
    • 隐式拒绝(抛出了异常)
    const dataPromise = new Promise((resolve, reject) => {
        const NUM = 10;
        // 隐式拒绝,给const变量重新赋值抛出错误
        NUM = 100;
    });
    
    dataPromise.then(
        data => console.log(data)
    ).catch(    // 这里的链式调用catch接收一个错误处理函数,
                // 与将函数写在then第二个参数的效果一样
        err => console.log(err)
        // TypeError: Assignment to constant variable.
    );
    

    4.5 创建一个真实promise案例

    function getJSON(url) {
        return new Promise((resolve, reject) => {
            const request = new XMLHttpRequest();
    
            // 初始化请求
            request.open("GET", url);
    
            // 当服务器响应后会被调用
            request.onload = function () {
                // JSON代码容易出现语法错误,所以把对JSON.parse抱在try-catch中
                try {
                    // 服务器状态码为200表示一切正常
                    if (this.status === 200) {
                        resolve(JSON.parse(this.response));
                    } else {
                        reject(this.status + " " + this.statusText);
                    }
                } catch (e) {
                    reject(e.message);
                }
    
                // 和服务器通信过程中发生错误后会被调用
                request.onerror = function () {
                    reject(this.status + " " + this.statusText);
                }
            }
    
            // 发送请求
            request.send();
        });
    };
    
    getJSON("./students.json").then(
        data => {
            // 数据处理
        }
    ).catch(err => console.log(err));
    

    本例中有3个潜在的错误源:客户端与服务器之间的连接错误、服务器返回错误的数据(无效响应状态码)、无效的JSON代码

    4.6 链式调用promise处理相互依赖的异步任务序列

    // 每次调用getJSON都会返回一个promise对象,
    // 因此可以链式调用then,以顺序执行多个步骤
    getJSON(url)
        .then(() => {})
        .then(() => {})
        .then(() => {})
        .catch(() => {});
    // catch可以捕获任何步骤中产生的错误
    

    4.7 Promise.all处理多个独立的异步任务

    // Promise.all接收一个promise对象数组
    // 其中只要一个被拒绝,则所有被拒绝
    Promise.all([getJSON("./data/students.json"),
                 getJSON("./data/teachers.json"),
                 getJSON("./data/classes.json")])
                 .then(results => {
                 // 结果以数组的形式顺序返回
                     const students = results[0];
                     const teachers = results[1];
                     const classes = results[2];
                     console.log(students !== null);
                     console.log(teachers !== null);
                     console.log(classes !== null);
                 }).catch(err => console.log(err));
    

    4.8 Promise.race处理第一个成功(或失败)的promise

    // 方法返回一个全新的promise对象
    // 一旦数组中某一个promise被处理或被拒绝,
    // 这个返回的promise同样会被处理或被拒绝
    Promise.race([getJSON("./data/students.json"),
                  getJSON("./data/teachers.json"),
                  getJSON("./data/classes.json")]).then(result => {
                      console.log(result);
                  }).catch(err => console.log(err));
    
    

    5. 把生成器和promise相结合

    // 仅作演示,不推荐使用
    function async(genertor){
        // 创建迭代器控制生成器
        const iterator = genertor();
    
        // 处理生成器产生的值
        function handle(iteratorResult){
    
            // 没有新值产生就直接返回
            if(iteratorResult.done) { return; }
    
            const iteratorValue = iteratorResult.value;
    
            if(iteratorValue instanceof Promise){
                // 处理生成器返回的promise对象,
                // 用next方法发送数据给生成器并处理下一个返回的promise对象
                iteratorValue.then(res => handle(iterator.next(res)))
                             .catch(err => iterator.throw(err));
            }
        }
    
        try {
            handle(iterator.next());
        }
        catch(err) {iterator.throw(e)}
    }
    
    async(function* () {
        try {
            // 等待异步结果返回时暂停
            // 对每个异步任务执行yield
            const classes = yield getJSON("./data/classes.json");
            const teachers = yield getJSON(classes.classes[0].teachers);
            const students = yield getJSON(teachers.teachers[0].students);
            // 处理数据。。。 
        }
        catch (err) {console.log(err)};
    });
    

    6. 面向未来的async函数

    async是ES8新增特性,本书并未详细讲解

    // async关键字表明当前函数依赖一个异步返回的值
    (async function(){
        try {
            // 每一个调用异步任务的位置上,都要放置await关键字,
            // 来告诉JS引擎,在不阻塞应用执行的情况下在这个位置等待执行结果
            const classes = await getJSON("./data/classes.json");
            const teachers = await getJSON(classes.classes[0].teachers);
            
            console.log(teachers.teachers[0].subject);
        }
        catch(e) {console.log(e)}
    })();
    
  • 相关阅读:
    idea 连接 hive
    css img自适应
    测试视频文件
    ubuntu不显示ipv4地址的解决办法
    nginx path捕获
    union all两个结果集报ORA-12704: character set mismatch错误
    润乾报表试用指南
    报表工具对比之润乾报表与锐浪报表对比
    项目微管理36
    docker远程调用
  • 原文地址:https://www.cnblogs.com/hycstar/p/14020109.html
Copyright © 2011-2022 走看看