uni-app async/await优化启动流程

App启动流程

通常我们App在进入首页时,都需要做一些初始化工作,如申请权限,从服务器获取数据(user信息,App配置信息等),sdk初始化等。一般的App可能包含下面几个步骤:

  1. 用户协议和隐私政策(首次启动展示,需要用户同意才可进入app)
  2. 权限申请
  3. 用户登录状态判断
    3.1 未登录跳转登录页面
    3.2 已登录获取用户信息
  4. app基础配置信息获取(首页banner配置,部分活动配置等)
  5. 获取的完成后进入首页。

需求流程图

流程图

一种实现方式

针对上述需求,我们可以这样实现

<template>
    <view class="content">
        <image class="logo" src="/static/logo.png"></image>
        <view class="text-area">
            <text class="title">text</text>
        </view>
        <protocolDialog ref="protocol_dialog" title="个人信息保护指引" protocolPath='/pages/pp/protocol'
            policyPath='/pages/pp/policy' @protocol_confirm="protocol_confirm"></protocolDialog>
        <permissionDialog ref="pd_dialog" @permissionDialogCancel="permissionDialogCancel"></permissionDialog>
    </view>
</template>


export default {
    onReady() {
        const isAgreeProtocol = uni.getStorageSync("agree_protocol");
        if (isAgreeProtocol) {
            this.requestPermission();
        } else {
            this.$refs.protocol_dialog.open();
            uni.setStorageSync("agree_protocol", true);
        }
    },
    methods: {
        //用户协议弹框用户选择同意
        protocol_confirm(b) {
            if (b) {
                this.requestPermission();
            }
        },
        //申请权限
        requestPermission() {
            const that = this;
            permision.requestAndroidPermission("android.permission.CAMERA")
                .then(res => {
                    if (res == -1) {
                        //被永久拒绝权限
                        this.$refs.pd_dialog.open();
                    } else {
                        const uid = uni.getStorageSync("uid");
                        const token = uni.getStorageSync("token");
                        if (!uid || !token) {
                            uni.redirectTo({
                                url: './login'
                            })
                            return;
                        } else {
                            that.getUser();
                        }
                    }
                }).catch(err => {

                })
        },
        //权限弹框选择取消
        permissionDialogCancel(b) {
            const uid = uni.getStorageSync("uid");
            const token = uni.getStorageSync("token");
            if (!uid || !token) {
                uni.redirectTo({
                    url: './login'
                })
                return;
            } else {
                this.getUser();
            }
        },
        //模拟用户登录
        getUser(uid, token) {
            setTimeout(() => {
                const result = {
                    "uid": "123456",
                    "name": "张三"
                }
                uni.redirectTo({
                    url: './home'
                })
            }, 2000)
        },
    }
}

上述实现方式可以满足需求,但是方法嵌套严重。
这样带来两个问题:

  1. 可读行差
  2. 可扩展性差

当启动流程有更改时,需要改动较多代码。
如在权限申请步骤后添加模拟器判断步骤,那么需要修改的方法有permissionDialogCancel,requestPermission,以及新添加方法checkSimulator。

然后由于Android应用市场较多且规则不一,因而启动流程往往会有改动需求
比如“华为应用市场”要求,用户拒绝权限也必须能进入app体验相关内容
再比如“应用宝”要求,在用户同意隐私权限之后才可以进行网络请求,获取设备信息等操作
由于需求会有改动。这就要求我们的相关代码具有高扩展性。

Async/Await改进方法

使用Async/Await,可以将异步操作变成了同步的形式,逻辑更清晰。
针对上述代码,主要封两点优化

弹框组件使用Promise方式

之前使用了传统的prop,$emit方法,使得弹框的展示和确认逻辑分散了。使用Promise方法如下,
先看下最终的调用效果

this.$refs.protocol_dialog.openModal().then(res=>{}).catch(err=>{}); 

采用这样的方式,就可以使用async和await进行改进。
弹框具体实现逻辑如下:

<template name="reward-video-dialog">
    <view @touchmove.stop.prevent="clear" v-show="isShow">
        <view class="popup_mask" @touchmove.stop.prevent="clear"></view>
        <view class="modal-content">
            <view class='modal_title'>提示</view>
            <view class='modal_textarea'>
                为了给您提供更好的体验,请打开“手机拍照”权限,否则无法进入App哦!
            </view>

            <view class='buy_vip' @click='openPermission'>
                打开权限
            </view>

            <view class='buy_count' @click='cancel'>
                取消
            </view>
        </view>
    </view>
</template>
<script>
    import permision from "@/js_sdk/wa-permission/permission.js"
    let permissions = []
    
    export default {

        data() {
            return {
                isShow: false
            }
        },
        
        onShow() {
            console.log("permissionDialog show");
        },

        methods: {
            clear() {
                return;
            },
            openPermission(){
                permision.gotoAppPermissionSetting();
            },
            open(){
                this.isShow = true;
            },
            
            
            openModal(){
                this.isShow = true;
                return new Promise((resolve,reject)=>{
                    uni.$on("permissionDialog" , function(res){
                        resolve(res);
                    })
                    uni.$on("permissionDialogError",function(){
                        reject();
                    })
                })
            },
            cancel(){
                this.$emit("permissionDialogCancel" , false);
                uni.$emit("permissionDialog" , false);
                this.isShow = false;
            }
        }
    }
</script>

引用弹框的时候,既可以用传统的方式,也可以用Promise方式。

异步请求Promise包装

 //模拟用户登录
    getUser(uid , token){
        return new Promise((resolve , reject)=>{
            setTimeout(()=>{
                const result = {
                    "uid":"123456",
                    "name":"张三"
                }
                resolve(result);
            },2000)
        })
    },

这部分比较简单,就是Promise的基本用法。

最终方案

    onReady() {
        this.start();
    },
    methods: {
        async start(){
            //隐私权限
            const isAgreeProtocol = uni.getStorageSync("agree_protocol");
            if(!isAgreeProtocol){
                await this.$refs.protocol_dialog.openModal();
                uni.setStorageSync("agree_protocol" , true);
            }
            //权限申请
            // const hasPermission = permision.judgeIosPermission("location");
            // console.log(hasPermission);
                const requestResult = await permision.requestAndroidPermission("android.permission.CAMERA");
                console.log("requestResult = " +requestResult);
                if(requestResult == -1){
                    const hasPermission = await this.$refs.pd_dialog.openModal();
                }
                
            //获取用户信息
            const uid = uni.getStorageSync("uid");
            const token = uni.getStorageSync("token");
            if(!uid || !token){
                uni.redirectTo({
                    url:'./login'
                })
                return;
            }
            await Promise.all([this.getUser(uid , token) , this.getOtherRequest()]);
            uni.redirectTo({
                url:'./home'
            })
        },
        //模拟用户登录
        getUser(uid , token){
            return new Promise((resolve , reject)=>{
                setTimeout(()=>{
                    const result = {
                        "uid":"123456",
                        "name":"张三"
                    }
                    resolve(result);
                },2000)
            })
        },
        
        //模拟网络请求
        getOtherRequest(){
            return new Promise((resolve , reject)=>{
                setTimeout(()=>{
                    resolve(1);
                },2000)
            })
        }

使用async/await后,不在有方法的嵌套,看起来就是同步的代码,可读性强。同时,若流程有修改,如之前的添加模拟器检测步骤,那么这次只需要添加模拟器方法,然后在start()方法相应位置添加即可。

//检测模拟器
checkSimulator(){
    return new Promise((resolve , reject)=>{
        setTimeout(()=>{
            resolve(true);
        },2000)
    })
}

//start方法添加模拟器判断
const isSimulator = await this.checkSimulator();
if(isSimulator){
    uni.showToast({
    title:'禁止使用模拟器'
    }
    )
    return;
}
//获取用户信息
const uid = uni.getStorageSync("uid");
const token = uni.getStorageSync("token");
if(!uid || !token){
    uni.redirectTo({
        url:'./login'
    })
    return;
}

有不好或者不对的地方欢迎指正
源码地址:https://gitee.com/BIN_CUH/uni-start-opt

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,324评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,356评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,328评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,147评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,160评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,115评论 1 296
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,025评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,867评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,307评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,528评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,688评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,409评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,001评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,657评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,811评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,685评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,573评论 2 353

推荐阅读更多精彩内容