• 如何利用 promise 影响代码的执行顺序?


    如何利用 promise 影响代码的执行顺序?

    我们写代码的时候,经常会遇到这样的场景。2个不同组件,它们的生命周期本身都是相互独立,毫无关联的,但是它们调用的接口,存在相互依赖的情况。

    我举个例子:

    开发小程序时候,里面 App 有一个 onLaunchhook,在小程序初始化时调用,而 Page 里也有一个 onLoadhook,在页面加载时被调用。正常的执行顺序为:

    // 应用启动
    onLaunch(options)
    // do sth....
    // 页面加载
    onLoad(query) 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    但是,我们往往也经常遇到这种 case:

    async onLaunch(){store.dispatch('set-token',await getToken())
    }
    async onLoad(){// getUserInfo 依赖 tokensetUserInfo(await getUserInfo())
    } 
    
    • 1
    • 2
    • 3
    • 4

    现在问题来了,依据上面的执行顺序,getTokengetUserInfo 请求实际上是并发执行的。而我们的预期是,先执行 getToken 并设置好全局 token 值之后,才调用 getUserInfo,这样后端才能依据请求携带的,用户 token 信息,来给我们返回指定的数据,不然那就只有一个 401 了。

    那么我们如何让它们之间产生调用的依赖关系呢? 实际上很简单 promiseevent-emitter 都是方法之一。接下来我们来构建一个最小化模型。

    最小化模型

    我们想要 onLoad一部分的代码的执行在 onLaunch特定代码之后。即把一部分并行跑的代码,变更为串行的顺序,同时也允许原先并行运行的方式。

    根据描述,我们天然的就想到了 Microtask,它运行在每个事件循环的执行代码,和运行Task后,Rerender 前。

    接下来为了实现期望,我们就需要在 onLaunch 中去产生 Promise,然后到 onLoad 中依据 Promise 状态的变化,执行代码。

    那么我们就很容易在一个文件中,构建出一个最小化模型,见下方代码:

    let promise
    
    function producer () {console.log('producer start!')promise = new Promise((resolve) => {setTimeout(() => {console.log('promise resolved')resolve(Math.random())}, 2_000)})console.log('producer end!')
    }
    
    async function consumer () {console.log('consumer start!')console.log(await promise)console.log('consumer end!')
    }
    
    producer()
    consumer() 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    这段代码中,我在 producer 创建了一个 promise,在 2sresolve 一个随机数,然后再在 consumer 中,去 await 它的状态,变为 fulfilled 后打印 consumer end!。 当然 async/await 只是语法糖,你用 then/catch 也是可以的,不过使用 await 有一个好处就是,它在面对非 Promise 对象的时候,它会自动把值进行包裹转化成 Promise,即 Promise.resolve(value)

    接着,让我们把这个模型进行扩充,变为多文件模型。

    // ref.js 创建一个引用
    export default {promise: undefined
    } 
    
    • 1
    • 2
    • 3
    // producer.js
    import ref from './ref.js'
    
    export default () => {console.log('producer start!')ref.promise = new Promise((resolve) => {setTimeout(() => {console.log('promise resolved')resolve(Math.random())}, 2_000)})console.log('producer end!')
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    // consumer.js
    import ref from './ref.js'
    
    export default async () => {console.log('consumer start!')console.log(await ref.promise)console.log('consumer end!')
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    // index.js
    import producer from './producer.js'
    import consumer from './consumer.js'
    
    producer()
    consumer() 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    执行结果同理。

    移花接木

    根据上述的代码,我们就可以对小程序的开发,进行一系列劫持的操作。我们以 uni-app vue2/3 和原生为例。

    // vue2
    Vue.mixin({created () {if (Array.isArray(this.$options.onLoad) && this.$options.onLoad.length) {this.$options.onLoad = this.$options.onLoad.map(fn => {return async (params:Record) => {await ref.promisefn.call(this, params)}})}}
    })
    
    // vue3
    const app = createSSRApp(App)
    app.mixin({created () {if (this.$scope) {const originalOnLoad = this.$scope.onLoadthis.$scope.onLoad = async (params:Record) => {await ref.promiseoriginalOnLoad.call(this, params)}}}
    })
    
    // native
    const nativePage = Page
    Page = function (options: Parameters[0]) {if (options.onLoad && typeof options.onLoad === 'function') {const originalOnLoad = options.onLoadoptions.onLoad = async function (params: Record) {await ref.promiseoriginalOnLoad.call(this, params)}}nativePage(options)
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    思路其实都差不多。

    增强

    上述的方法,虽然达到了目的,但是实在太简陋了,扩展性也很差。

    我们以 ref.js 为例,里面只放了一个 promise 太浪费了,为什么不把它放入全局状态里去呢?这样随时可以取出来进行观察。

    为什么不创建多个 Promise queue 呢? 这样还能循环往复地利用不同的队列,来作为代码执行的信道,同时又能够自定义并发度,超时,执行事件间隔等等。p-queue 就是不错的选择。

    当然,这些也只是抛砖引玉,这些相信大家各自有各自的看法,反正先做到满足当前的需求,再根据进阶的需求进行适当的改造,做出来的才是最适合自己的。

    最后

    最近找到一个VUE的文档,它将VUE的各个知识点进行了总结,整理成了《Vue 开发必须知道的36个技巧》。内容比较详实,对各个知识点的讲解也十分到位。



    有需要的小伙伴,可以点击下方卡片领取,无偿分享

  • 相关阅读:
    ChatGPT工作提效之使用python开发对接百度地图开放平台API的实战方案(批量路线规划、批量获取POI、突破数量有限制、批量地理编码)
    Linux系统安装DB2数据库的详细步骤
    程序公司分红及退出机制设计(1)
    MySQL常用操作命令大全
    spring面试常遇见的问题(02)
    竞赛 基于深度学习的人脸性别年龄识别 - 图像识别 opencv
    5年Java面试阿里P6经验总结
    Oracle 平均数详解(avg)
    git rebase与git merge图文详解(一文看懂区别)
    基于GBDT+Tkinter+穷举法按排队时间预测最优路径的智能导航推荐系统——机器学习算法应用(含Python工程源码)+数据集(四)
  • 原文地址:https://blog.csdn.net/web2022050903/article/details/127864479