资讯专栏INFORMATION COLUMN

JavaScript异步编程:帮助弗利萨完成8次变身

avwu / 1513人阅读

摘要:请帮助弗利萨完全变身成第形态,击败赛亚人。而弗利萨只要在每次变身成功或者失败时发出通知就行了。让弗利萨在变身完成或失败时,通过这个,告知所有收看该节目的观众他变身失败,或者成功了。让弗利萨开始变身前往查看示例代码

这边文章试图通过一个例子展示javascript异步编程的几种写法。
示例说明

弗利萨必须要从第1形态过渡到第8形态才有可能击败赛亚人,每一次变身成下一形态需要1秒钟,在这期间他可能会遭受到赛亚人的攻击,如果在变身过程中受到伤害,他将被打回第一形态。请帮助弗利萨完全变身成第8形态,击败赛亚人。

弗利萨的单例对象

开始创建一个弗利萨

 const Frieza = (function () {
            var state = 1; //内部变量,表示弗利萨的当前形态
            return {
                chargingFlag: false, //表示弗利萨是否正在变身中
                damage: function () {  //20%几率收到伤害
                    return Math.random() < 0.2;
                },
                stateReset: function () { //打回第一形态
                    state = 1;
                },
                getState: function () { //外部访问state的接口函数
                    return state;
                },
                change: function (callback) { //变身

                    if (this.chargingFlag === true) { 
                        throw new Error(`弗利萨还没变身完毕呢`);
                    }
                    this.chargingFlag = true;
                    console.log(`弗利萨开始进行第${state + 1}形态变身`)
                    setTimeout(() => { //每一阶段变身消耗1秒
                        if (this.damage()) {
                            this.stateReset();
                            this.chargingFlag = false;
                            callback("变身被悟空打断啦!");
                            return;
                        }
                        state++;
                        this.chargingFlag = false;
                        callback(null, state);
                    }, 1000)
                }

            }
        })();

以上代码用立即执行函数创建了一个弗利萨

state为内部变量,表示弗利萨当前的形态,初始为第一形态,并且设置了一个接口(getState函数)供外部了解弗利萨当前的形态。

change函数实现了弗利萨的变身,必须等到一次变身完毕后才能再次变身,每一次变身需要1秒钟。

在每一次变身完毕后会执行回调函数,我们规定回调函数有两个参数,第一个表示变身失败,被打断时应当传入的参数,第二个表示变身成功时应当传入的参数。

接下来需要写一些代码来帮助弗利萨锲而不舍的变身,直到他成功变身到第8形态。示例最终会按下图的样子在控制台中呈现。

用Promise帮助弗利萨:
        function keepChange() {
            return new Promise((resolve, reject) => {
                Frieza.change((err, state) => {
                    if (err) {
                        reject(err);
                    } else {
                        resolve(state);
                    }
                })
            })
        }
        function handelKeepChange() {
            keepChange().then((x) => {
             if(x !== 8){
                handelKeepChange();
             } else {
                 console.log("成功!")
             }
        }).catch(err => {
            console.log(err);
            handelKeepChange();
        })
        }
       handelKeepChange();

看上去已经不错了,这已经比直接在回调函数里面写回调函数要好得多了,我们通过递归调用handelKeepChange,让这条Promise链持续到第八次变身完毕。

用Promise + 生成器帮助弗利萨
 // generator + promise

        function* async() {
            while (Frieza.getState() !== 8) {
                yield keepChange();
            }
            console.log("成功!");
        }
        function keepChange() {
            return new Promise((resolve, reject) => {
                Frieza.change((err, state) => {
                    if (err) {
                        reject(err);
                    } else {
                        resolve(state);
                    }
                })
            })
        }
        function handleAsync(asyncFn) {
            const ita = asyncFn();
            function handle(v) {
                if (!v.done) {
                    v.value.then(state => {
                        handle(ita.next(state));
                    }).catch(err => {
                        console.log(err);
                        handle(ita.next());
                    })
                }
            }
            handle(ita.next());
        }
        handleAsync(async);

 

这种用生成器+promise的写法比纯用promise的写法要复杂一些,但是因为利用了生成器的特性,使得我们在执行具体的异步业务时,可以写的比较优雅:

        function* async() {
            while (Frieza.getState() !== 8) {
                yield keepChange();
            }
            console.log("成功!");
        }

这种写法比较有亲和力,逻辑上比较清晰。它内部的实现是通过一个handleAsync函数不断地递归调用handle函数,从而让生成器能在一次Promis承诺实现后让生成器继续产出下一次Promise。

            function handle(v) {
                if (!v.done) { // 如果生成器还没结束,那么就继续产出一个promise
                    v.value.then(state => {
                        handle(ita.next(state));
                    }).catch(err => {
                        console.log(err);
                        handle(ita.next());
                    })
                }
            }
用async await帮助弗利萨

async await是promise+生成器的语法层面实现。可以让我们省略背后的细节,直接采用同步写法编写异步程序。

        async function handleAsync() {
            while(Frieza.getState() !== 8){
                try {
                    await keepChange();
                } catch (error) {
                    console.log(error)
                }
            }
            console.log("成功!");
           
        } 
        handleAsync(); */

这样就可以了,几乎与promise+与生成器的业务写法一模一样。

用rxjs帮助弗利萨

用上rxjs的观察者模式后,实际上就可以把弗利萨的change函数里面的callback给解耦出来。把这部分的逻辑交给观察者处理里面。而弗利萨只要在每次变身成功或者失败时发出通知就行了。
具体步骤如下

创建一个可以供大家收看的电视节目"dragonBall",这个被我们叫做七龙珠的电视节目(subject)可以被观众们订阅,同时,这个电视节目也能随心所欲的播放他想要给观众们看到的东西。

const dragonBall = new Rx.Subject();

让弗利萨在变身完成或失败时,通过dragnonBall这个subject,告知所有收看该节目的观众他变身失败,或者成功了。修改弗利萨的change函数:

            change: function () {

                if (this.chargingFlag === true) {
                    drangonBall.next(new Error("变身还没结束呢!"))
                }
                this.chargingFlag = true;
                console.log(`弗利萨开始进行第${state + 1}形态变身`)
                setTimeout(() => {
                    if (this.damage()) {
                        this.stateReset();
                        this.chargingFlag = false;
                        dragonBall.next(new Error("变身被悟空打断啦!"));
                        return;
                    }
                    state++;
                    this.chargingFlag = false;
                    dragonBall.next(`${state}形态变身成功!`)
                }, 1000)
            }

收看dragonBall,并且在弗利萨没变到第8形态前,持续地让弗利萨变身。

        const watchAnime = dragonBall.asObservable()
        
        .subscribe(message => {
            console.log(message);
            
            if (Frieza.getState() !== 8) {
                Frieza.change();
            } else {
                watchAnime.unsubscribe();
            }

        })

让弗利萨开始变身

   Frieza.change();

前往github查看示例代码

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/95592.html

相关文章

  • JavaScript 编程精解 中文第三版 四、数据结构:对象和数组

    摘要:本章将介绍基本的数据结构。松鼠人一般在晚上八点到十点之间,雅克就会变身成为一只毛茸茸的松鼠,尾巴上的毛十分浓密。我们将雅克的日记表示为对象数组。 来源:ApacheCN『JavaScript 编程精解 中文第三版』翻译项目原文:Data Structures: Objects and Arrays 译者:飞龙 协议:CC BY-NC-SA 4.0 自豪地采用谷歌翻译 部分参考了《Jav...

    kamushin233 评论0 收藏0
  • 从云计算到边缘计算 打造更智能的未来

    摘要:年月日,美国,西雅图微软年度开发者盛会在美国西雅图开幕,微软公司首席执行官萨提亚纳德拉微软全球执行副总裁兼云计算与企业事业部负责人微软全球执行副总裁兼人工智能及微软研究事业部负责人沈向洋,与数千名来自全球各地的开发者齐聚一堂。2017年5月10日,美国,西雅图——微软年度开发者盛会Build 2017在美国西雅图开幕,微软公司首席执行官萨提亚 纳德拉、微软全球执行副总裁兼云计算与企业事业部负...

    oneasp 评论0 收藏0
  • 搬瓦工,DC2(QN)机房CN2 VPS测评,可选机房洛杉矶(QN,MC)/弗利蒙/新泽西/荷兰

    摘要:瓦工的机房,电信为双程线路,可选机房为洛杉矶弗利蒙新泽西荷兰这个机房,带宽为口,最低配的月流量为。相比较和价格相仿,但是配置更高,额外还赠送免费快照,免费自动备份等。瓦工的dc2机房,电信为双程cn2线路,可选机房为洛杉矶(QN,MC)/弗利蒙/新泽西/荷兰这5个机房,带宽为G口,最低配的月流量为1T。相比较和dc3价格相仿,但是配置更高,额外还赠送免费快照,免费自动备份等。 ...

    miguel.jiang 评论0 收藏0
  • JS笔记

    摘要:从最开始的到封装后的都在试图解决异步编程过程中的问题。为了让编程更美好,我们就需要引入来降低异步编程的复杂性。异步编程入门的全称是前端经典面试题从输入到页面加载发生了什么这是一篇开发的科普类文章,涉及到优化等多个方面。 TypeScript 入门教程 从 JavaScript 程序员的角度总结思考,循序渐进的理解 TypeScript。 网络基础知识之 HTTP 协议 详细介绍 HTT...

    rottengeek 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<