Skip to content

Latest commit

 

History

History
103 lines (81 loc) · 3.29 KB

03.记录一次用队列解决问题.md

File metadata and controls

103 lines (81 loc) · 3.29 KB

上下文

最近工作,与原生app混合开发。简言之,就是在原生app的webview中开发业务。其中碰到了一个坑点。

由于页面有接口鉴权,我们需要调用原生app通过jsBridge注入的方法(GET_TOKEN),获取账户token。

(以下代码均做了简化,非真实业务代码)

function GET_TOKEN() {
  return new Promise(resolve => {
    if (BROWSER.isClient) {
      WinJSRdy(_ => window.WinJSBridge.call('authtoken', 'getauthtoken', ({ response: { authToken } }) => {
        resolve(authToken)
      }))
    } else if (isDEV) {
      resolve(DEV_TOKEN)
    } else {
      resolve('')
    }
  })
}

最开始,并没有多想,封装了fetch方法

function REQUEST_WITH_TOKEN(url) {
    return GET_TOKEN().then(token=>fetch(url,{headers: { Authorization: token }}))
}

但是当我们这样调用

REQUEST_WITH_TOKEN('url1')
REQUEST_WITH_TOKEN('url2')
REQUEST_WITH_TOKEN('url3')

发现只有第一次会执行。原来客户端做了防抖处理,在短时间内多次调用客户端的方法,客户端会只执行一次。

😂😂😂

只能想办法解决了。最先想到的就是将多次请求依次放入.then中,但这样丑且改变了业务逻辑,所以放弃了。

此时我想到了react源码里经常使用的队列思想,用在此处,可以将多次请求放入一个队列,待token获取完后,按先进先出,依次执行。

开始改写

首先写一个调度器

let _token = ''
let isGetting = false
let scheduler = {
  queue: [],
  next: val => {
    while (scheduler.queue.length > 0) {
      let firstFn = scheduler.queue.shift()
      firstFn(val)
    }
    isGetting = false
    _token = ''
  },
  listen: callback => {
    scheduler.queue.push(callback)
  },
}

接着生成队列

if (_token) {
  return fetch(url,{headers:{Authorization:_token}})
} else {
  let hasPromise =  new Promise(resolve => {
      scheduler.listen(token => {
        resolve(token)
      })
    }).then(_token => {
      return fetch(url,{headers:{Authorization:_token}})
  })
  if (!isGetting) {
    isGetting = true
    GET_TOKEN().then(token => {
      _token = token
      scheduler.next(token)
    })
  }
  return hasPromise
}

当第一次请求发起时,_token为空,生成hasPromise,这个Promise通过scheduler注册一个回调,监听下一个token产生时resolve(token),同时isGetting为false,说明没有去取token,此时调用GET_TOKEN.then中调用调度器的next方法,让调度器产生一个token,并执行queue中的回调,队列执行完后,回归初始状态,清空token,重置isGetting为false,其他请求则生成hasPromise返回。这样短时间内发起的多次请求,只会调用一次GET_TOKEN方法。

这里的关键是,调度器通过listen将每一个Promise的resolvepush进了一个队列,把控了resolve调用时机。

React相关

react的源码里的高级技巧比比皆是,有时见得识不得,有时识得用不得。一方面的确源码太绕,相对于业务,无法有直观感受,一方面投入链太长,回报又不明朗,很难长期坚持阅读。这次问题的解决,也是长期浸染在react此类问题的文章中,才有的思路。