如何优化编写「快应用」数据存储(storage)?

快应用 May 12, 2022

查阅快应用开发文档,可以知道官方提供了诸多接口 ;对于如何使用,文档中也给出了简单的说明和代码示例;但照着示例写法,在实际项目中,不够优雅,且更不高效;所以,在开发中需要加以变通去写,使得大幅提升开发效率,同时也令整个代码优雅,以便于维护。早期有写文章: 如何优雅使用「快应用」API ,探讨了优雅调用 API。就其数据存储(storage),发现也有必要单开一篇,以探讨如何优雅编写相关代码。

不太优雅的写法

在官方文档: 数据存储(storage) ,给出了如下编写示例:

import storage from '@system.storage'
storage.get({
  key: 'YOUR-KEY',
  success: function(data) {
    console.log('handling success')
  },
  fail: function(data, code) {
    console.log(`handling fail, code = ${code}`)
  },
  complete: function(data) {
    console.log(`handling complete`, data)
  }
})

这种写法本身呢,倒也没什么问题,只是显得很有历史感(属于当代不推荐的写法);但,遇到些奇特的诉求,这种写法的劣势就非常明显,给初级开发者,做了极其不负责任的示范。譬如说,可能处于某种诉求考虑,就有可能写出类似如下代码(示例 X Code):

import storage from '@system.storage'

onInit() {
    setTimeout(() => {
        storage.get({
          key: 'YOUR-KEY',
          success: function(data) {
            console.log('handling success')
            setTimeout(() => {
                // do something again.
            }, 100)
          },
          fail: function(data, code) {
            console.log(`handling fail, code = ${code}`)
          },
          complete: function(data) {
            console.log(`handling complete`, data)
          }
        })
    }, 100)
}

弊端可谓显而易见,那么该如何优化下呢?

更为推荐的写法

大致的思路是,要充分利用 Promisestorage API 进行封装;再借助 asyncawait ,对异步代码进行链式调用,从而使得代码去除冗杂的回调,使其更加清晰、简洁。具体步骤如下:

封装 storage API

// helper/storage.js
import storage from '@system.storage'
import prompt from '@system.prompt'

export default {
  set: (key, value) => {
    return new Promise((resolve, reject) => {
      return storage.set({
        key,
        value,
        success: function(data) {
	      // 统一对存储的内容,做处理;
          resolve(data)
        },
        fail: function(data, code) {
          console.log(`Somthing Error[@storage set]: data = ${data}`)
          reject(data)
        }
      })
    })
  },

  get: key => {
    return new Promise((resolve, reject) => {
      return storage.get({
        key,
        success: function(data = '[]') {
          try {
            resolve(JSON.parse(data || '[]') || {})
          } catch (error) {
            prompt.showToast({
              message: `获取 storage 数据出现问题,${error.message}`
            })
            console.log(`Somthing Error[@storage get]: error = ${error}`)
            resolve([])
          }
        },
        fail: function(data, code) {
          console.log(`Somthing Error[@storage get]: data = ${data}`)
          reject(data)
        }
      })
    })
  }
}

改为链式调用

再说明链式调用之前,有必要对 setTimeout 方法做下说明;改函数会在计时器到期后执行;setTimeout() 是一个异步函数,这意味着定时器函数不会暂停函数堆栈中其他函数的执行。换句话说,您不能 setTimeout() 在函数堆栈中的下一个函数触发之前创建“暂停”。为程序简洁化考虑,也可以对其进行封装,以在合适场景,按需使用。

// 方法 1
function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

// 方法 2
const sleep = m => new Promise(r => setTimeout(r, m))

// 或直接使用
await new Promise(resolve => setTimeout(resolve, 1000));

经过如上优化之后,先前的示例 X Code 中代码,可优化为如下代码:

import storage from "/helper/storage"

async onInit() {
    await sleep(100)
    const data = await storage.get({ key })
    await sleep(100)
    // do something again.
}

最后,回顾下这样写所带来的优势:

  • 移除不必要回调,使整个逻辑,更加清晰、简洁、优雅;
  • 对 Storage API 做了封装, 可统一对存储的内容做处理;

当然,这种思路并不仅限于快应用开发, 在 Web 、小程序开发中,同样适用。以上优化,在开源项目 graceful-sentences-quickapp 有完整呈现,感兴趣的朋友,可了解一下。

您可能会感兴趣的文章

Tags

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.