Preload.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. import {reporter} from "./Reporter.js"
  2. import {modelTable} from "./ModelTable.js"
  3. import util from "./util.js"
  4. import {http1} from "./Http1.js"
  5. import InternalError from "./error/InternalError.js"
  6. import axios from "axios";
  7. import Logger from "./Logger.js"
  8. import PreloadCanceledError from "./error/PreloadCanceledError.js"
  9. import ParamError from "./error/ParamError.js"
  10. const logger = new Logger('preload')
  11. export default class Preload {
  12. constructor(e) {
  13. this.config = null
  14. this.allKeys = []
  15. this.oldResourcesDeleted = !1;
  16. this.requests = {
  17. simple: {
  18. stopped: !0,
  19. requests: {}
  20. },
  21. observer: {
  22. stopped: !0,
  23. requests: {}
  24. },
  25. full: {
  26. stopped: !0,
  27. requests: {}
  28. }
  29. };
  30. this.modelManager = e,
  31. this.init(e.appId)
  32. }
  33. init(e) {
  34. reporter.updateBody({
  35. appId: e
  36. })
  37. }
  38. static getTimeoutBySize(e) {
  39. return e ? e < 500 * 1e3 ? 30 * 1e3 : e < 1e3 * 1e3 ? 60 * 1e3 : 100 * 1e3 : 100 * 1e3
  40. }
  41. async getConfig(e) {
  42. if (this.config)
  43. return this.config;
  44. const {preload: t} = await this.modelManager.requestConfig();
  45. return t ? (this.config = t,
  46. Promise.resolve(t)) : Promise.reject("no preload config")
  47. }
  48. async getAllKeys() {
  49. if (this.allKeys.length)
  50. return this.allKeys;
  51. try {
  52. const e = await modelTable.getAllKeys();
  53. this.allKeys = e;
  54. return e
  55. } catch {
  56. const t = "preload getAllKeys error";
  57. return logger.error(t),
  58. Promise.reject(t)
  59. }
  60. }
  61. stop(e) {
  62. e === "serverless" && (e = "observer"),
  63. this.requests[e].stopped = !0;
  64. const t = this.requests[e].requests;
  65. Object.keys(t).forEach(r=>{
  66. http1.canceler.removePending(r),
  67. delete t[r]
  68. }
  69. )
  70. }
  71. clearPreload(e) {
  72. this.requests[e].stopped = !1,
  73. this.allKeys = []
  74. }
  75. async start(e, t, r) {
  76. let n = Date.now()
  77. , o = 0;
  78. try {
  79. if (e === "serverless" && (e = "observer"),
  80. !this.requests[e])
  81. return Promise.reject(new ParamError("invalid stage name: " + e));
  82. this.clearPreload(e);
  83. const a = await this.getConfig(e);
  84. const s = await this.getAllKeys();
  85. try {
  86. await this.deleteOldResources(a.assetUrls.map(d=>d.url), s)
  87. } catch (d) {
  88. logger.error(d)
  89. }
  90. const {baseUrls: l, assetUrls: u, observeUrls: c} = a;
  91. let h;
  92. switch (e) {
  93. case "simple":
  94. h = l;
  95. break;
  96. case "observer":
  97. h = u;
  98. break;
  99. case "full":
  100. h = u;
  101. break;
  102. default:
  103. h = u
  104. }
  105. let f = h.filter(d=>!s.includes(d.url));
  106. r && isFunction(r) && (f = f.filter(r));
  107. o = f.length;
  108. logger.debug("keysNeedToPreload", f);
  109. f.length || t && t(h.length, h.length);
  110. n = Date.now();
  111. await this._preload(e, f, t);
  112. logger.infoAndReportMeasurement({
  113. tag: e,
  114. type: "assetsPreload",
  115. extraData: {
  116. total: o
  117. }
  118. });
  119. return
  120. } catch (a) {
  121. let s = a;
  122. return (this.requests[e].stopped || axios.isCancel(a)) && (s = new PreloadCanceledError),
  123. logger.infoAndReportMeasurement({
  124. tag: e,
  125. type: "assetsPreload",
  126. error: s,
  127. options: {
  128. immediate: !0
  129. },
  130. extraData: {
  131. total: o
  132. }
  133. }),
  134. Promise.reject(s)
  135. }
  136. }
  137. deleteOldResources(e, t) {
  138. if (!this.oldResourcesDeleted)
  139. this.oldResourcesDeleted = !0;
  140. else
  141. return Promise.resolve();
  142. const r = t.filter(n=>!e.includes(n));
  143. return logger.debug("keysNeedToDelete", r),
  144. logger.warn("keysNeedToDelete", r.length),
  145. Promise.all(r.map(n=>modelTable.delete(n)))
  146. }
  147. async _preload(e, t, r) {
  148. const n = t.length;
  149. if (!n)
  150. return Promise.resolve();
  151. let o = 0;
  152. const a = window.setInterval(()=>{
  153. r && r(o, n),
  154. o >= n && window.clearInterval(a)
  155. }
  156. , 1e3);
  157. return util.mapLimit(t, 10, async s=>{
  158. const {size: l, url: u} = s;
  159. return this.requests[e].stopped ? Promise.reject(new PreloadCanceledError) : http1.get({
  160. url: u,
  161. timeout: Preload.getTimeoutBySize(l),
  162. responseType: "blob",
  163. retry: 2,
  164. beforeRequest: ()=>{
  165. this.requests[e].requests[u] = !0
  166. }
  167. }).then(async c=>{
  168. const h = c.data;
  169. if (!(h instanceof Blob))
  170. return logger.error("request blob failed, type:", typeof h, u),
  171. Promise.reject("request blob failed " + u);
  172. const f = await blobToDataURI(h);
  173. try {
  174. await modelTable.put({
  175. url: u,
  176. model: f
  177. });
  178. return
  179. } catch (d) {
  180. return logger.error("unable to add data to indexedDB", d),
  181. Promise.reject(new InternalError("preload db error"))
  182. }
  183. }
  184. ).then(()=>{
  185. o++,
  186. delete this.requests[e].requests[u]
  187. }
  188. , c=>(delete this.requests[e].requests[u],
  189. window.clearInterval(a),
  190. Promise.reject(c)))
  191. }
  192. )
  193. }
  194. }