Monty

Monty 查看完整档案

广州编辑  |  填写毕业院校  |  填写所在公司/组织 blog.mting.net 编辑
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 个人简介什么都没有

个人动态

Monty 赞了回答 · 今天 17:20

该怎么跟后端沟通

  1. 跟懂的人沟通(找他的技术方向的领导,说明你的感受和顾虑)
  2. 找业务负责人(产品经理项目经理,说明这样做性能、安全上面的问题)
  3. 如果对方是单兵作战的,自信心爆棚,别人说不服的。 妥协,坐等项目暴雷。

关注 16 回答 13

Monty 提出了问题 · 2月22日

该怎么跟后端沟通

公司新来的后端同学,写接口喜欢返回很多前端不需要的参数,给我的感觉是恨不得把1个接口把整个数据库都带过来。

常常问他要接口了,他说之前某某页面已经返回过这个数据了,你直接用就行了。

他甚至连用户的用户名和密码都明文带过来了,让我无力吐槽。说是后台前端管理系统要显示。

这种情况,该怎么跟后端沟通?

关注 16 回答 13

Monty 赞了文章 · 2月18日

微信新增功能「小程序外链」,短信营销场景如何落地实现营销闭环

在微信小程序支持外部网站跳转到小程序的功能后,我们第一时间上线了 PC 端的小程序外链生成器和小程序桌面快捷方式生成器,受到了广大小程序开发者和产品运营的称赞和应用。为了让大家以更高的效率、更低的成本,获得更显著的营销效果,本周我们给大家带来两个好消息。

  • 小程序外链支持移动端配置,只需一部手机,随时随地进行多渠道引流;
  • 支持短信内容带小程序外链,快速设置短信内容,一键完成消息发送。

如果你不是很了解小程序外链,可查看文章《两步实现桌面图标、短信和邮件外链跳转到小程序,轻松召回沉睡用户》。

小程序外链

小程序外链是微信小程序近期上线的新功能,支持从外部渠道(邮件、短信、网页、其它 App、手机桌面快捷方式等)快速跳转到微信小程序平台上。

小程序外链的应用场景

短信、邮件内容营销

在短信、邮件内容中附上小程序链接,用户点击链接直接跳转到微信小程序指定页面上,有效降低用户操作门槛,提高营销效果。

网页(贴吧、社区、论坛等)

支持从网页(如贴吧、社区、论坛等)链接跳转到微信小程序,实现多渠道营销引流。

其它 App(QQ、抖音等)

支持在 QQ、抖音等 App 的聊天窗口中,通过小程序外链极速打开微信小程序,无需切换微信客户端。

手机桌面快捷方式

通过「小程序桌面快捷方式生成器」可以快速将小程序添加到手机桌面上,用户无需打开微信客户端,点击图标一秒访问小程序。

你可以将已生成的小程序外链(URL)和「小程序桌面快捷方式生成器」分享给你的用户,引导他们将你的小程序添加到手机桌面上,快捷的访问方式有利于提高小程序活跃度,减少用户流失。

image

小程序外链生成器使用步骤

移动端

关注知晓云公众号,点击菜单「知晓云」-「小程序外链」跳转到小程序链页面,如果你是首次使用知晓云服务,则需注册账号、创建企业和授权微信小程序,如已有账号,则登录账号即可,首次使用小程序外链功能需更新授权小程序。

点击「添加外链」按钮生成小程序外链(URL),点击「复制」按钮,即可将外链应用到邮件、短信等营销场景中。你还可以针对不同渠道、不同小程序页面创建不同的外链,实现营销渠道的效果分析。

image

PC 端

登录知晓云控制台,选择你要设置的应用,点击「微信」-「小程序链」-「添加」按钮,输入相关信息生成小程序外链(URL)。

image

短信推送添加小程序外链

知晓云已支持短信内容快速插入小程序外链,一键推送短信功能。生成小程序外链后,你可以直接使用系统已为你准备好的短信模板,无需复杂的配置工作、无需审核、一键发送,一站式完成用户触达。

你可以在「知晓云控制台-运营-短信」中配置短信内容,并选择你已生成的小程序外链,即可一键发送给你的用户。

温馨提示:

  • 已经在知晓云授权过的小程序,需要更新授权,在授权页面中勾选「获取 URL Scheme 权限」
  • 单个小程序的外链数量无上限;
  • 目前小程序外链暂不支持个人版的小程序;
  • 小程序未上线则无法生成外链;
  • 添加小程序桌面快捷方式目前仅支持 iOS 自带浏览器和安卓的小米自带浏览器、谷歌浏览器。

更新预告

小程序外链功能还在不断迭代中,如果你有其他想法,欢迎你随时跟我们交流。

  • 微信小程序视频播放器;
  • 支持快手小程序、车载小程序,全平台支持能力再升级;
  • 知晓推送支持 iOS 和 Android 。
查看原文

赞 2 收藏 1 评论 0

Monty 赞了文章 · 2月18日

基于uni-app编写的登录模板,request请求封装,全局路由拦截,也可作为项目基础模板使用

前言

做一个新的项目就需要常用的代码,比如

    • 登录,注册 (模板)
    • 全局路由守卫(路由配置)
    • Request封装(请求封装)
    • api集中管理
    • 引入依赖的UI库(color-ui,uview-ui)
    • flex常用布局css,
    • 配置分包
    • utils常用工具函数
    • 配置Vuex(store)
    • 等等

    为了提高自己的效率(说白了有点懒)决定把这些作为基础模板,下次做新项目直接用就行了

    效果图

    在这里插入图片描述

    全局路由守卫

    (1) 路由拦截

    uni-simple-router 路由、拦截、最优雅的解决方案

    (2) 路由配置

    通过 vue.config.js 配合uni-read-pages,可以随心所欲的读取 pages.json 下的所有配置

    Request封装

    适用于一项目多域名请求、七牛云图片上传、本地服务器图片上传、支持 Promise.

    api集中管理

    api集中管理; 为简化逻辑代码量整洁的原则,像调用函数一样调用api,做到代码分离,在apis目录统一创建api函数,并且封装接口返回数据类型校验的方法,挂载到vue原型中,页面通过this.$apis.apiName()调用

    分包

    sub目录分包管理 由于微信小程序的限制,上传发布机制总包大小不能大于2m,所以项目若超出该限制,要在page.json中做分包处理,分包处理的配置与pages目录保持一致

    配置vuex

    不需要引入每个子store模块

    import Vue from "vue";
    import Vuex from "vuex";
    
    Vue.use(Vuex);
    const files = require.context("./modules", false, /\.js$/);
    let modules = {
        state: {},
        mutations: {},
        actions: {}
    };
    
    files.keys().forEach((key) => {
      Object.assign(modules.state, files(key)["state"]);
      Object.assign(modules.mutations, files(key)["mutations"]);
      Object.assign(modules.actions, files(key)["actions"]);
    });
    const store = new Vuex.Store(modules);
    export default store;

    页面使用Vuex

    import { mapState,mapActions } from 'vuex';
    
    computed: {
                ...mapState(['userInfo'])
            }
    methods: {
                ...mapActions(['getUserInfo'])
            }
            

    源码下载

    后面持续更新。。。。

    作者相关文章

    反编译获取任何微信小程序源码——看这篇就够了

    抽签小程序,妈妈再也不用担心谁洗碗(分配任务)了,so easy

    5分钟实现微信云小程序支付功能(含源码)

    查看原文

    赞 12 收藏 7 评论 0

    Monty 提出了问题 · 2020-12-25

    adb刚connect设备就offline

    adb 通过wifi连接远程设备,上午还好好的。下午就各种offline。

    zzt ~/D/U/box> adb connect 192.168.1.53
    connected to 192.168.1.53:5555
    zzt ~/D/U/box> adb shell
    error: device offline
    zzt ~/D/U/box> 

    反复的重启adb服务但是依然无法解决问题

    adb kill-server
    adb start-server
    adb remount

    能connect上,但所有的操作都是offline,有人遇到过同样问题么,该怎么去解决?

    关注 1 回答 0

    Monty 收藏了文章 · 2020-11-04

    Dcloud,hbuilderX开发uni-app第一天踩坑记录

    其实大部分坑在 uni-app在官网都有介绍 具体位置在 在 uni-app 中使用 Vue.js 模块

    官方文档中总结了很多坑,但我只说一下我今天遇到的:

    坑1:uni-app不支持vue中的过滤器

    解决办法:从后台获取数据后始用js对数据进行处理,
    例子:

    始用过滤器时:

    <div v-for="talk in talkList">
        <p>{{talk.date|formatTime}}</p>
    </div>

    始用uni-app:

    uni.request({
            url: 'http://localhost:8088/talk/queryList', //仅为示例,并非真实接口地址。
            success: (res) => {
                this.talkList = res.data
                this.talkList.forEach(item => item.date = this.formatTime(item.date))
            }
        })
    

    坑2: uni-app中vuex使用的区别

    uni-app中this.$store为undefind ,必须要在main.js中加入这行代码

    Vue.prototype.$store = store
    

    详细配置可点击标题连接,uni-app官网有详细说明,与普通vue项目不同的只是多了上面这行代码

    坑3:微信不支持本地字体图标

    之前我的iconfont.css是从 阿里巴巴矢量图标库 下载到本地的,但是uni-app不支持本地iconfont.css,报错

    00:42:22.580 Module build failed: ModuleNotFoundError: Module not found: Error: Can't resolve './iconfont.eot?t=1521557349802' in 'D:\workspace\appProjects\uniQingchi\pages\index'
    00:42:22.592     at factoryCallback (D:\app\HBuilderX\plugins\uniapp\node_modules\webpack\lib\Compilation.js:264:39)
    00:42:22.592     at factory (D:\app\HBuilderX\plugins\uniapp\node_modules\webpack\lib\NormalModuleFactory.js:247:20)
    00:42:22.603     at resolver (D:\app\HBuilderX\plugins\uniapp\node_modules\webpack\lib\NormalModuleFactory.js:65:21)
    00:42:22.613     at asyncLib.parallel (D:\app\HBuilderX\plugins\uniapp\node_modules\webpack\lib\NormalModuleFactory.js:138:21)
    

    后来看了官网知道了微信小程序不支持本地图标,

    解决方案:从阿里巴巴矢量图标库 获取在线连接

    clipboard.png

    可以点击Unicode旁边的Font class然后点开里面的网址,将展示的内容替换本地的css就好了

    坑4:普通vue项目代码粘过来改动很大,不支持html原生标签,类似微信小程序

    而且目前uni-app 标签很少,组件也很少, uni-app中的view标签相当于html中的div或者p标签,text标签相当于p标签,
    我今天只用了三个标签 button,view,image
    贴一个官方的代码你们自己感受一下 全是一色的view标签:

    clipboard.png

    查看原文

    Monty 收藏了文章 · 2020-10-29

    理解 JavaScript 的 async/await

    2020-06-04 更新

    JavaScript 中的 async/await 是 AsyncFunction 特性 中的关键字。目前为止,除了 IE 之外,常用浏览器和 Node (v7.6+) 都已经支持该特性。具体支持情况可以在 这里 查看。


    我第一次看到 async/await 这组关键字并不是在 JavaScript 语言里,而是在 C# 5.0 的语法中。C# 的 async/await 需要在 .NET Framework 4.5 以上的版本中使用,因此我还很悲伤了一阵——为了要兼容 XP 系统,我们开发的软件不能使用高于 4.0 版本的 .NET Framework。

    我之前在《闲谈异步调用“扁平”化》 中就谈到了这个问题。无论是在 C# 还是 JavaScript 中,async/await 都是非常棒的特性,它们也都是非常甜的语法糖。C# 的 async/await 实现离不开 Task 或 Task\<Result\> 类,而 JavaScript 的 async/await 实现,也离不开 Promise

    现在抛开 C# 和 .NET Framework,专心研究下 JavaScript 的 async/await。

    1. async 和 await 在干什么

    任意一个名称都是有意义的,先从字面意思来理解。async 是“异步”的简写,而 await 可以认为是 async wait 的简写。所以应该很好理解 async 用于申明一个 function 是异步的,而 await 用于等待一个异步方法执行完成。

    另外还有一个很有意思的语法规定,await 只能出现在 async 函数中。然后细心的朋友会产生一个疑问,如果 await 只能出现在 async 函数中,那这个 async 函数应该怎么调用?

    如果需要通过 await 来调用一个 async 函数,那这个调用的外面必须得再包一个 async 函数,然后……进入死循环,永无出头之日……

    如果 async 函数不需要 await 来调用,那 async 到底起个啥作用?

    1.1. async 起什么作用

    这个问题的关键在于,async 函数是怎么处理它的返回值的!

    我们当然希望它能直接通过 return 语句返回我们想要的值,但是如果真是这样,似乎就没 await 什么事了。所以,写段代码来试试,看它到底会返回什么:

    async function testAsync() {
        return "hello async";
    }
    
    const result = testAsync();
    console.log(result);

    看到输出就恍然大悟了——输出的是一个 Promise 对象。

    c:\var\test> node --harmony_async_await .
    Promise { 'hello async' }

    所以,async 函数返回的是一个 Promise 对象。从文档中也可以得到这个信息。async 函数(包含函数语句、函数表达式、Lambda表达式)会返回一个 Promise 对象,如果在函数中 return 一个直接量,async 会把这个直接量通过 Promise.resolve() 封装成 Promise 对象。

    补充知识点 [2020-06-04]

    Promise.resolve(x) 可以看作是 new Promise(resolve => resolve(x)) 的简写,可以用于快速封装字面量对象或其他对象,将其封装成 Promise 实例。

    async 函数返回的是一个 Promise 对象,所以在最外层不能用 await 获取其返回值的情况下,我们当然应该用原来的方式:then() 链来处理这个 Promise 对象,就像这样

    testAsync().then(v => {
        console.log(v);    // 输出 hello async
    });

    现在回过头来想下,如果 async 函数没有返回值,又该如何?很容易想到,它会返回 Promise.resolve(undefined)

    联想一下 Promise 的特点——无等待,所以在没有 await 的情况下执行 async 函数,它会立即执行,返回一个 Promise 对象,并且,绝不会阻塞后面的语句。这和普通返回 Promise 对象的函数并无二致。

    那么下一个关键点就在于 await 关键字了。

    1.2. await 到底在等啥

    一般来说,都认为 await 是在等待一个 async 函数完成。不过按语法说明,await 等待的是一个表达式,这个表达式的计算结果是 Promise 对象或者其它值(换句话说,就是没有特殊限定)。

    因为 async 函数返回一个 Promise 对象,所以 await 可以用于等待一个 async 函数的返回值——这也可以说是 await 在等 async 函数,但要清楚,它等的实际是一个返回值。注意到 await 不仅仅用于等 Promise 对象,它可以等任意表达式的结果,所以,await 后面实际是可以接普通函数调用或者直接量的。所以下面这个示例完全可以正确运行

    function getSomething() {
        return "something";
    }
    
    async function testAsync() {
        return Promise.resolve("hello async");
    }
    
    async function test() {
        const v1 = await getSomething();
        const v2 = await testAsync();
        console.log(v1, v2);
    }
    
    test();

    1.3. await 等到了要等的,然后呢

    await 等到了它要等的东西,一个 Promise 对象,或者其它值,然后呢?我不得不先说,await 是个运算符,用于组成表达式,await 表达式的运算结果取决于它等的东西。

    如果它等到的不是一个 Promise 对象,那 await 表达式的运算结果就是它等到的东西。

    如果它等到的是一个 Promise 对象,await 就忙起来了,它会阻塞后面的代码,等着 Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果。

    看到上面的阻塞一词,心慌了吧……放心,这就是 await 必须用在 async 函数中的原因。async 函数调用不会造成阻塞,它内部所有的阻塞都被封装在一个 Promise 对象中异步执行。

    2. async/await 帮我们干了啥

    2.1. 作个简单的比较

    上面已经说明了 async 会将其后的函数(函数表达式或 Lambda)的返回值封装成一个 Promise 对象,而 await 会等待这个 Promise 完成,并将其 resolve 的结果返回出来。

    现在举例,用 setTimeout 模拟耗时的异步操作,先来看看不用 async/await 会怎么写

    function takeLongTime() {
        return new Promise(resolve => {
            setTimeout(() => resolve("long_time_value"), 1000);
        });
    }
    
    takeLongTime().then(v => {
        console.log("got", v);
    });

    如果改用 async/await 呢,会是这样

    function takeLongTime() {
        return new Promise(resolve => {
            setTimeout(() => resolve("long_time_value"), 1000);
        });
    }
    
    async function test() {
        const v = await takeLongTime();
        console.log(v);
    }
    
    test();

    眼尖的同学已经发现 takeLongTime() 没有申明为 async。实际上,takeLongTime() 本身就是返回的 Promise 对象,加不加 async 结果都一样,如果没明白,请回过头再去看看上面的“async 起什么作用”。

    又一个疑问产生了,这两段代码,两种方式对异步调用的处理(实际就是对 Promise 对象的处理)差别并不明显,甚至使用 async/await 还需要多写一些代码,那它的优势到底在哪?

    2.2. async/await 的优势在于处理 then 链

    单一的 Promise 链并不能发现 async/await 的优势,但是,如果需要处理由多个 Promise 组成的 then 链的时候,优势就能体现出来了(很有意思,Promise 通过 then 链来解决多层回调的问题,现在又用 async/await 来进一步优化它)。

    假设一个业务,分多个步骤完成,每个步骤都是异步的,而且依赖于上一个步骤的结果。我们仍然用 setTimeout 来模拟异步操作:

    /**
     * 传入参数 n,表示这个函数执行的时间(毫秒)
     * 执行的结果是 n + 200,这个值将用于下一步骤
     */
    function takeLongTime(n) {
        return new Promise(resolve => {
            setTimeout(() => resolve(n + 200), n);
        });
    }
    
    function step1(n) {
        console.log(`step1 with ${n}`);
        return takeLongTime(n);
    }
    
    function step2(n) {
        console.log(`step2 with ${n}`);
        return takeLongTime(n);
    }
    
    function step3(n) {
        console.log(`step3 with ${n}`);
        return takeLongTime(n);
    }

    现在用 Promise 方式来实现这三个步骤的处理

    function doIt() {
        console.time("doIt");
        const time1 = 300;
        step1(time1)
            .then(time2 => step2(time2))
            .then(time3 => step3(time3))
            .then(result => {
                console.log(`result is ${result}`);
                console.timeEnd("doIt");
            });
    }
    
    doIt();
    
    // c:\var\test>node --harmony_async_await .
    // step1 with 300
    // step2 with 500
    // step3 with 700
    // result is 900
    // doIt: 1507.251ms

    输出结果 resultstep3() 的参数 700 + 200 = 900doIt() 顺序执行了三个步骤,一共用了 300 + 500 + 700 = 1500 毫秒,和 console.time()/console.timeEnd() 计算的结果一致。

    如果用 async/await 来实现呢,会是这样

    async function doIt() {
        console.time("doIt");
        const time1 = 300;
        const time2 = await step1(time1);
        const time3 = await step2(time2);
        const result = await step3(time3);
        console.log(`result is ${result}`);
        console.timeEnd("doIt");
    }
    
    doIt();

    结果和之前的 Promise 实现是一样的,但是这个代码看起来是不是清晰得多,几乎跟同步代码一样

    2.3. 还有更酷的

    现在把业务要求改一下,仍然是三个步骤,但每一个步骤都需要之前每个步骤的结果。

    function step1(n) {
        console.log(`step1 with ${n}`);
        return takeLongTime(n);
    }
    
    function step2(m, n) {
        console.log(`step2 with ${m} and ${n}`);
        return takeLongTime(m + n);
    }
    
    function step3(k, m, n) {
        console.log(`step3 with ${k}, ${m} and ${n}`);
        return takeLongTime(k + m + n);
    }

    这回先用 async/await 来写:

    async function doIt() {
        console.time("doIt");
        const time1 = 300;
        const time2 = await step1(time1);
        const time3 = await step2(time1, time2);
        const result = await step3(time1, time2, time3);
        console.log(`result is ${result}`);
        console.timeEnd("doIt");
    }
    
    doIt();
    
    // c:\var\test>node --harmony_async_await .
    // step1 with 300
    // step2 with 800 = 300 + 500
    // step3 with 1800 = 300 + 500 + 1000
    // result is 2000
    // doIt: 2907.387ms

    除了觉得执行时间变长了之外,似乎和之前的示例没啥区别啊!别急,认真想想如果把它写成 Promise 方式实现会是什么样子?

    function doIt() {
        console.time("doIt");
        const time1 = 300;
        step1(time1)
            .then(time2 => {
                return step2(time1, time2)
                    .then(time3 => [time1, time2, time3]);
            })
            .then(times => {
                const [time1, time2, time3] = times;
                return step3(time1, time2, time3);
            })
            .then(result => {
                console.log(`result is ${result}`);
                console.timeEnd("doIt");
            });
    }
    
    doIt();

    有没有感觉有点复杂的样子?那一堆参数处理,就是 Promise 方案的死穴—— 参数传递太麻烦了,看着就晕!

    3. 洗洗睡吧

    就目前来说,已经理解 async/await 了吧?但其实还有一些事情没提及——Promise 有可能 reject 啊,怎么处理呢?如果需要并行处理3个步骤,再等待所有结果,又该怎么处理呢?

    阮一峰老师已经说过了,我就懒得说了。

    4. 推荐相关文章

    5. 来跟边城(作者)学 更新@2020-11-14

    TypeScript从入门到实践 【2020 版】

    TypeScript从入门到实践 【2020 版】

    6. 关于转载 补充@2020-03-05

    常有读者问是否可以转载。

    笔者表示欢迎各位转载,但转载时一定注明作者和出处,谢谢!


    公众号-边城客栈
    请关注公众号 边城客栈

    看完了先别走,点个赞啊 ⇓,赞赏 ⇘ 也行!

    查看原文

    Monty 赞了文章 · 2020-10-29

    理解 JavaScript 的 async/await

    2020-06-04 更新

    JavaScript 中的 async/await 是 AsyncFunction 特性 中的关键字。目前为止,除了 IE 之外,常用浏览器和 Node (v7.6+) 都已经支持该特性。具体支持情况可以在 这里 查看。


    我第一次看到 async/await 这组关键字并不是在 JavaScript 语言里,而是在 C# 5.0 的语法中。C# 的 async/await 需要在 .NET Framework 4.5 以上的版本中使用,因此我还很悲伤了一阵——为了要兼容 XP 系统,我们开发的软件不能使用高于 4.0 版本的 .NET Framework。

    我之前在《闲谈异步调用“扁平”化》 中就谈到了这个问题。无论是在 C# 还是 JavaScript 中,async/await 都是非常棒的特性,它们也都是非常甜的语法糖。C# 的 async/await 实现离不开 Task 或 Task\<Result\> 类,而 JavaScript 的 async/await 实现,也离不开 Promise

    现在抛开 C# 和 .NET Framework,专心研究下 JavaScript 的 async/await。

    1. async 和 await 在干什么

    任意一个名称都是有意义的,先从字面意思来理解。async 是“异步”的简写,而 await 可以认为是 async wait 的简写。所以应该很好理解 async 用于申明一个 function 是异步的,而 await 用于等待一个异步方法执行完成。

    另外还有一个很有意思的语法规定,await 只能出现在 async 函数中。然后细心的朋友会产生一个疑问,如果 await 只能出现在 async 函数中,那这个 async 函数应该怎么调用?

    如果需要通过 await 来调用一个 async 函数,那这个调用的外面必须得再包一个 async 函数,然后……进入死循环,永无出头之日……

    如果 async 函数不需要 await 来调用,那 async 到底起个啥作用?

    1.1. async 起什么作用

    这个问题的关键在于,async 函数是怎么处理它的返回值的!

    我们当然希望它能直接通过 return 语句返回我们想要的值,但是如果真是这样,似乎就没 await 什么事了。所以,写段代码来试试,看它到底会返回什么:

    async function testAsync() {
        return "hello async";
    }
    
    const result = testAsync();
    console.log(result);

    看到输出就恍然大悟了——输出的是一个 Promise 对象。

    c:\var\test> node --harmony_async_await .
    Promise { 'hello async' }

    所以,async 函数返回的是一个 Promise 对象。从文档中也可以得到这个信息。async 函数(包含函数语句、函数表达式、Lambda表达式)会返回一个 Promise 对象,如果在函数中 return 一个直接量,async 会把这个直接量通过 Promise.resolve() 封装成 Promise 对象。

    补充知识点 [2020-06-04]

    Promise.resolve(x) 可以看作是 new Promise(resolve => resolve(x)) 的简写,可以用于快速封装字面量对象或其他对象,将其封装成 Promise 实例。

    async 函数返回的是一个 Promise 对象,所以在最外层不能用 await 获取其返回值的情况下,我们当然应该用原来的方式:then() 链来处理这个 Promise 对象,就像这样

    testAsync().then(v => {
        console.log(v);    // 输出 hello async
    });

    现在回过头来想下,如果 async 函数没有返回值,又该如何?很容易想到,它会返回 Promise.resolve(undefined)

    联想一下 Promise 的特点——无等待,所以在没有 await 的情况下执行 async 函数,它会立即执行,返回一个 Promise 对象,并且,绝不会阻塞后面的语句。这和普通返回 Promise 对象的函数并无二致。

    那么下一个关键点就在于 await 关键字了。

    1.2. await 到底在等啥

    一般来说,都认为 await 是在等待一个 async 函数完成。不过按语法说明,await 等待的是一个表达式,这个表达式的计算结果是 Promise 对象或者其它值(换句话说,就是没有特殊限定)。

    因为 async 函数返回一个 Promise 对象,所以 await 可以用于等待一个 async 函数的返回值——这也可以说是 await 在等 async 函数,但要清楚,它等的实际是一个返回值。注意到 await 不仅仅用于等 Promise 对象,它可以等任意表达式的结果,所以,await 后面实际是可以接普通函数调用或者直接量的。所以下面这个示例完全可以正确运行

    function getSomething() {
        return "something";
    }
    
    async function testAsync() {
        return Promise.resolve("hello async");
    }
    
    async function test() {
        const v1 = await getSomething();
        const v2 = await testAsync();
        console.log(v1, v2);
    }
    
    test();

    1.3. await 等到了要等的,然后呢

    await 等到了它要等的东西,一个 Promise 对象,或者其它值,然后呢?我不得不先说,await 是个运算符,用于组成表达式,await 表达式的运算结果取决于它等的东西。

    如果它等到的不是一个 Promise 对象,那 await 表达式的运算结果就是它等到的东西。

    如果它等到的是一个 Promise 对象,await 就忙起来了,它会阻塞后面的代码,等着 Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果。

    看到上面的阻塞一词,心慌了吧……放心,这就是 await 必须用在 async 函数中的原因。async 函数调用不会造成阻塞,它内部所有的阻塞都被封装在一个 Promise 对象中异步执行。

    2. async/await 帮我们干了啥

    2.1. 作个简单的比较

    上面已经说明了 async 会将其后的函数(函数表达式或 Lambda)的返回值封装成一个 Promise 对象,而 await 会等待这个 Promise 完成,并将其 resolve 的结果返回出来。

    现在举例,用 setTimeout 模拟耗时的异步操作,先来看看不用 async/await 会怎么写

    function takeLongTime() {
        return new Promise(resolve => {
            setTimeout(() => resolve("long_time_value"), 1000);
        });
    }
    
    takeLongTime().then(v => {
        console.log("got", v);
    });

    如果改用 async/await 呢,会是这样

    function takeLongTime() {
        return new Promise(resolve => {
            setTimeout(() => resolve("long_time_value"), 1000);
        });
    }
    
    async function test() {
        const v = await takeLongTime();
        console.log(v);
    }
    
    test();

    眼尖的同学已经发现 takeLongTime() 没有申明为 async。实际上,takeLongTime() 本身就是返回的 Promise 对象,加不加 async 结果都一样,如果没明白,请回过头再去看看上面的“async 起什么作用”。

    又一个疑问产生了,这两段代码,两种方式对异步调用的处理(实际就是对 Promise 对象的处理)差别并不明显,甚至使用 async/await 还需要多写一些代码,那它的优势到底在哪?

    2.2. async/await 的优势在于处理 then 链

    单一的 Promise 链并不能发现 async/await 的优势,但是,如果需要处理由多个 Promise 组成的 then 链的时候,优势就能体现出来了(很有意思,Promise 通过 then 链来解决多层回调的问题,现在又用 async/await 来进一步优化它)。

    假设一个业务,分多个步骤完成,每个步骤都是异步的,而且依赖于上一个步骤的结果。我们仍然用 setTimeout 来模拟异步操作:

    /**
     * 传入参数 n,表示这个函数执行的时间(毫秒)
     * 执行的结果是 n + 200,这个值将用于下一步骤
     */
    function takeLongTime(n) {
        return new Promise(resolve => {
            setTimeout(() => resolve(n + 200), n);
        });
    }
    
    function step1(n) {
        console.log(`step1 with ${n}`);
        return takeLongTime(n);
    }
    
    function step2(n) {
        console.log(`step2 with ${n}`);
        return takeLongTime(n);
    }
    
    function step3(n) {
        console.log(`step3 with ${n}`);
        return takeLongTime(n);
    }

    现在用 Promise 方式来实现这三个步骤的处理

    function doIt() {
        console.time("doIt");
        const time1 = 300;
        step1(time1)
            .then(time2 => step2(time2))
            .then(time3 => step3(time3))
            .then(result => {
                console.log(`result is ${result}`);
                console.timeEnd("doIt");
            });
    }
    
    doIt();
    
    // c:\var\test>node --harmony_async_await .
    // step1 with 300
    // step2 with 500
    // step3 with 700
    // result is 900
    // doIt: 1507.251ms

    输出结果 resultstep3() 的参数 700 + 200 = 900doIt() 顺序执行了三个步骤,一共用了 300 + 500 + 700 = 1500 毫秒,和 console.time()/console.timeEnd() 计算的结果一致。

    如果用 async/await 来实现呢,会是这样

    async function doIt() {
        console.time("doIt");
        const time1 = 300;
        const time2 = await step1(time1);
        const time3 = await step2(time2);
        const result = await step3(time3);
        console.log(`result is ${result}`);
        console.timeEnd("doIt");
    }
    
    doIt();

    结果和之前的 Promise 实现是一样的,但是这个代码看起来是不是清晰得多,几乎跟同步代码一样

    2.3. 还有更酷的

    现在把业务要求改一下,仍然是三个步骤,但每一个步骤都需要之前每个步骤的结果。

    function step1(n) {
        console.log(`step1 with ${n}`);
        return takeLongTime(n);
    }
    
    function step2(m, n) {
        console.log(`step2 with ${m} and ${n}`);
        return takeLongTime(m + n);
    }
    
    function step3(k, m, n) {
        console.log(`step3 with ${k}, ${m} and ${n}`);
        return takeLongTime(k + m + n);
    }

    这回先用 async/await 来写:

    async function doIt() {
        console.time("doIt");
        const time1 = 300;
        const time2 = await step1(time1);
        const time3 = await step2(time1, time2);
        const result = await step3(time1, time2, time3);
        console.log(`result is ${result}`);
        console.timeEnd("doIt");
    }
    
    doIt();
    
    // c:\var\test>node --harmony_async_await .
    // step1 with 300
    // step2 with 800 = 300 + 500
    // step3 with 1800 = 300 + 500 + 1000
    // result is 2000
    // doIt: 2907.387ms

    除了觉得执行时间变长了之外,似乎和之前的示例没啥区别啊!别急,认真想想如果把它写成 Promise 方式实现会是什么样子?

    function doIt() {
        console.time("doIt");
        const time1 = 300;
        step1(time1)
            .then(time2 => {
                return step2(time1, time2)
                    .then(time3 => [time1, time2, time3]);
            })
            .then(times => {
                const [time1, time2, time3] = times;
                return step3(time1, time2, time3);
            })
            .then(result => {
                console.log(`result is ${result}`);
                console.timeEnd("doIt");
            });
    }
    
    doIt();

    有没有感觉有点复杂的样子?那一堆参数处理,就是 Promise 方案的死穴—— 参数传递太麻烦了,看着就晕!

    3. 洗洗睡吧

    就目前来说,已经理解 async/await 了吧?但其实还有一些事情没提及——Promise 有可能 reject 啊,怎么处理呢?如果需要并行处理3个步骤,再等待所有结果,又该怎么处理呢?

    阮一峰老师已经说过了,我就懒得说了。

    4. 推荐相关文章

    5. 来跟边城(作者)学 更新@2020-11-14

    TypeScript从入门到实践 【2020 版】

    TypeScript从入门到实践 【2020 版】

    6. 关于转载 补充@2020-03-05

    常有读者问是否可以转载。

    笔者表示欢迎各位转载,但转载时一定注明作者和出处,谢谢!


    公众号-边城客栈
    请关注公众号 边城客栈

    看完了先别走,点个赞啊 ⇓,赞赏 ⇘ 也行!

    查看原文

    赞 1298 收藏 1100 评论 133

    Monty 提出了问题 · 2020-10-15

    为什么在android设备内部存储上看不到有些app的包文件?

    一般而言, android 设备在内部存储/Android/data/ 这个路径下, 都有跟应用包名相关的文件夹目录, 为什么同一个设备, 有些app有, 而有些app没有呢?

    关注 2 回答 1

    Monty 收藏了文章 · 2019-11-06

    为何小程序上线了,他们的内心却留下遗憾?

    前言

    出于多端投放和开放生态的考虑,闲鱼开始接入整个阿里小程序体系。闲鱼在9月份迅速上线了第一个小程序鱼塘小程序,由于刚接触不熟悉小程序体系,整体性能上有比较大的优化空间,主要表现在以下问题:

    • 小程序加载慢,低端 Android 机(Android vivo Y67)上首屏时间接近6s
    • 滚动卡顿,在 iPhone 7P 上滚动帧率平均在 40fps 左右
    • 滚动多屏数据之后 Tab 点击切换慢,在 iPhone 7P 上切换 Tab 等待时间 3-5 秒,瞬时帧率低于 30fps
      小程序由于其逻辑和渲染分离的架构特点,除传统 H5 优化手段之外还有其他不同点。本篇文章主要分析小程序构架对渲染的影响,以及鱼塘小程序下具体优化手段。

    小程序架构

    在分析具体优化 Case 之前我们先看下小程序架构,先要了解架构才能清楚如何去优化具体的业务代码。阿里小程序采用支付宝小程序的架构,这里引用一张支付宝小程序页面生命周期图。

    目前市场上所有小程序都采用逻辑(worker)和渲染(webview)分离的方式。这样带来的好处是:

    1. 能够满足对于外部应用管控的诉求,由于业务逻辑没有运行在 webview 上,所以无法通过浏览器的API直接操作渲染动作,意味着不能通过脚本做一些动态化操作。所有渲染相关操作都是通过 axml 来定义,外部应用进行更改都需要通过平台审核。
    2. 多个页面可以共享同一个 JS 运行环境,整个小程序生命周期可以共享全局应用上下文,接近 App 的开发体验。
    3. 页面渲染与业务逻辑分开执行,不会出现 JS 逻辑执行导致渲染卡住的情况,有利于提升整体渲染性能。

    但是这样也会造成一个显而易见的问题,页面性能强依赖 webview 和 worker 的通信效率:

    1. webview 和 worker 之间的通信是异步的。这意味着当我们调用 setData 时,数据变更不会立即体现到页面渲染上,而是需要从 worker 异步传输到 webview 。
    2. 数据传输时需要序列化为字符串,然后通过 evaluateJavascript 方式传输,数据大小会影响传输性能。

    小程序逻辑和渲染分离的架构造成它与传统 H5 性能优化方式上的一些差异。小程序性能优化可以参考看下官方推荐的一些性能优化建议,简单来讲需要特别注意 setData 操作的频次和传输数据,接下来我们结合鱼塘小程序具体案例一块探讨下。

    业务层优化

    鱼塘小程序是一个类似兴趣圈子下的内容聚合场景,用户在这里可以无限加载浏览内容,还会点击 Tab 切换浏览不同维度的内容。我们需要重点考虑小程序加载流畅度、滚动顺滑度以及 Tab 点击切换时候界面响应速度。之前版本的鱼塘小程序在低端 Android 机(Android vivo Y67)上首屏时间接近6s,在 iPhone 7P 上滚动帧率平均在 34fps - 60fps,点击 Tab 切换瞬时帧率在 30fps 左右,下面针对几个具体 Case 讨论解决方案。

    加载慢

    『BEFORE』

    『AFTER』

    问题表现

    从打开小程序到最终渲染出来经历了短暂的白屏

    原因分析

    加载整体耗时包括小程序容器初始化、数据请求以及请求结果返回到渲染,需要针对每个时期做优化

    优化手段

    • 引入小程序实例保活机制,降低小程序启动耗时
    • 将数据请求提前至 page.onLoad 中,请求阶段加入闲鱼 Loading 提示,通过交互减少用户焦虑感
    • 首屏数据离线化,在首屏数据到达前预渲染,在容器测请求提前至与小程序实例启动并行或更前
    • 将首屏数据进行拆分,初始化只 setData 可见视图对应的数据

    滚动卡顿

    『BEFORE』

    『AFTER』

    问题表现

    页面滚动掉帧感明显,粘手度低

    原因分析

    • 由于需要监听页面滚动距离触发顶部 Bar 显示,Page 层监听了 onScroll 事件,并在回调中频繁的调用 setData
    • 加载下一页 Feeds 的请求回调中,解析数据时多次调用 setData
    • Feeds 卡片内部监听了组件的 onAppear 和 onDisappear 事件,并在回调中调用 setData,目的是为了不重复发送曝光埋点

    优化手段

    针对小程序 webview 和 worker 通信的机制,我们需要减少 setData 的调用频率与传输数据大小。

    • 优化了 onScroll 回调逻辑,改为只有在部分时机(滚动距离在一定范围内)下才会触发 setData,且只做局部渲染
    • 加载下一页 Feeds 的请求回调优化了数据解析逻辑,只调用一次 setData,并参考官方优化建议使用 $spliceData 渲染长列表
    • 将 setData 的数据大小进行优化,只传输会影响视图的相关数据
    • 不再监听组件 onAppear 和 onDisappear 事件,改为监听组件的 onFirstAppear 事件,只有第一次 Appear 的时候才执行曝光操作

    Tab 切换响应慢

    『BEFORE』

    『AFTER』

    问题表现

    加载几页 Feeds 后,切换 Tab 开始出现明显卡顿,需等待 3-5 秒,部分 Android 机器上更为严重,偶尔会 Crash

    原因分析

    Tab 切换时在短时间内做了太多事情:切换 Tab Current 状态、销毁 Feeds 列表、展示 Loading 动画、发起数据请求 -> 渲染新列表,这样高并发大面积的内容更新导致小程序视图层数据消费阻塞,从而产生卡顿感。

    优化手段

    • 将 Tab 切换时的任务拆解开,分四个阶段进行:

      • 切换 Tab Current 状态,执行 Tab 切换动画
      • 在 Tab 切换动画完成后将页面滚动到 Tab 刚好 Sticky 住的位置
      • 销毁 Feeds 列表,展示 Loading 动画
      • 发起数据请求 -> 渲染
    • 经过这样的拆解,将之前的『高并发大面积』转换成了『分阶段可控制』的更新方式,随之带来的就是界面上的流畅

    容器层优化

    小程序容器通过离线缓存、数据预加载、小程序保活等机制来优化整体性能。然而在富交互场景中,webview 上的控件渲染会遇到很多性能瓶颈,目前阿里小程序支持在 webview 中内嵌 native 组件来提升整体性能,鱼塘小程序中有大量视频内容场景,使用的 video 组件就是 native 原生组件。这类组件脱离 webview 线程之外渲染,但是由于是覆盖在 webview 之上,所以在 webview 内无论怎样修改 z-index 都无法将元素覆盖在原生组件之上。

    为了解决这个问题,小程序框架同学又设计了 cover-view ,它可以覆盖在 native 组件之上,比如视频上方的播放按钮就可以用 cover-view 盖上去。最终线上鱼塘小程序通过同层渲染 video 组件之后用户侧体验有比较大的提升。

    总结与展望

    经过优化之后,目前线上鱼塘小程序相比之前版本有显著提升:

    针对这个业务场景下的小程序依然有很多可以继续优化空间,例如我们将每个鱼塘实例化独自小程序,这样可以针对每个鱼塘小程序去进行保活等。此外在小程序性能优化相关上,我们认为可以在研发阶段提供一个包含性能告警的容器,通过监听 setData 的调用频率与传输数据大小,对开发者一些可能会影响性能的代码写法进行提示。未来我们会持续在闲鱼小程序生态建设上发力,整合集团研发资源建立闲鱼小程序研发全链路最佳实践,提供外部服务商入驻开发三方小程序的良好体验。

    iPhone 11 Pro、卫衣、T恤等你来抽,马上来试试手气 https://www.aliyun.com/1111/2...


    本文作者:闲鱼技术-颂晨、玉缜

    阅读原文

    本文为云栖社区原创内容,未经允许不得转载。

    查看原文

    认证与成就

    • 获得 30 次点赞
    • 获得 12 枚徽章 获得 0 枚金徽章, 获得 3 枚银徽章, 获得 9 枚铜徽章

    擅长技能
    编辑

    开源项目 & 著作
    编辑

    (゚∀゚ )
    暂时没有

    注册于 2017-07-05
    个人主页被 526 人浏览