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

熵桥流沙
• 阅读 1719
这边文章试图通过一个例子展示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形态。示例最终会按下图的样子在控制台中呈现。

JavaScript异步编程:帮助弗利萨完成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给解耦出来。把这部分的逻辑交给观察者处理里面。而弗利萨只要在每次变身成功或者失败时发出通知就行了。
具体步骤如下

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

    const dragonBall = new Rx.Subject();
  2. 让弗利萨在变身完成或失败时,通过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)
                }
  3. 收看dragonBall,并且在弗利萨没变到第8形态前,持续地让弗利萨变身。

            const watchAnime = dragonBall.asObservable()
            
            .subscribe(message => {
                console.log(message);
                
                if (Frieza.getState() !== 8) {
                    Frieza.change();
                } else {
                    watchAnime.unsubscribe();
                }
    
            })
    
  4. 让弗利萨开始变身

       Frieza.change();

前往github查看示例代码
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Karen110 Karen110
3年前
一篇文章带你了解JavaScript日期
日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用
美凌格栋栋酱 美凌格栋栋酱
6个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Easter79 Easter79
3年前
swap空间的增减方法
(1)增大swap空间去激活swap交换区:swapoff v /dev/vg00/lvswap扩展交换lv:lvextend L 10G /dev/vg00/lvswap重新生成swap交换区:mkswap /dev/vg00/lvswap激活新生成的交换区:swapon v /dev/vg00/lvswap
Jacquelyn38 Jacquelyn38
4年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
熵桥流沙
熵桥流沙
Lv1
尔来四万八千岁,不与秦塞通人烟。
文章
9
粉丝
0
获赞
0