common.js 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874
  1. var comm = {
  2. data: {
  3. baseUrl: ''
  4. },
  5. /* ====================================================================================== */
  6. addEvent: function (ele, ev, fn) { // 添加事件的函数
  7. //针对IE浏览器
  8. if (ele.attachEvent) {
  9. ele.attachEvent('on' + ev, fn)
  10. }
  11. //针对FF与chrome
  12. else {
  13. ele.addEventListener(ev, fn, false)
  14. }
  15. },
  16. ajax: function (options) { // 封装ajax
  17. var _this = this;
  18. //异步请求对象的完成状态
  19. this.done = 0;
  20. this.format = function () {
  21. var now = new String(new Date().getTime());
  22. return now.substr(0, now.length - 3);
  23. }
  24. //格式化参数
  25. this.formatParams = function (data) {
  26. //获取地址参数
  27. var arr = [];
  28. for (var name in data) {
  29. arr.push(encodeURIComponent(name) + "=" + encodeURIComponent(data[name]));
  30. }
  31. arr.push("t=" + _this.format());//按分钟刷一次
  32. return arr.join("&");
  33. }
  34. //传入设置
  35. options = options || {};
  36. //请求方式
  37. options.type = (options.type || "GET").toUpperCase();
  38. options.dataType = options.dataType || "json";
  39. options.async = options.async || true;
  40. options.contentType = options.contentType || 'application/x-www-form-urlencoded';
  41. var params = _this.formatParams(options.data);
  42. if (options.contentType == "application/json;charset=utf-8") {
  43. params = JSON.stringify(options.data)
  44. }
  45. //创建异步请求对象 - 第一步
  46. var xhr;
  47. //w3c标准
  48. if (window.XMLHttpRequest) {
  49. xhr = new XMLHttpRequest();
  50. }
  51. //兼容IE6及以下
  52. else if (window.ActiveObject) {
  53. xhr = new ActiveXObject('Microsoft.XMLHTTP');
  54. }
  55. //连接 和 发送 - 第二步
  56. //判断是那种类型的请求
  57. //若是get请求
  58. if (options.type == "GET") {
  59. //参数拼接
  60. if (options.url.indexOf("?") == -1) sp = "?"; else sp = "&";
  61. //发送请求
  62. xhr.open("GET", options.url + sp + params, options.async);
  63. xhr.send(null);
  64. }
  65. //若是post请求
  66. else if (options.type == "POST") {
  67. //发送请求
  68. xhr.open("POST", options.url, options.async);
  69. //设置表单提交时的内容类型
  70. xhr.setRequestHeader("Content-Type", options.contentType);
  71. //参数配置
  72. xhr.send(params);
  73. }
  74. //接收 - 第三步
  75. xhr.onreadystatechange = function () {
  76. if (xhr.readyState == 4) {
  77. //状态码
  78. var status = xhr.status;
  79. //状态码表示成功时,执行成功回调函数
  80. if (status >= 200 && status < 300 || status == 304) {
  81. //返回数据的格式
  82. //json字符串
  83. if (options.dataType == "json") {
  84. try {
  85. options.success && options.success(eval("(" + xhr.responseText + ")"));
  86. }
  87. catch (err) {
  88. options.success && options.success(JSON.parse(xhr.responseText), xhr.responseXML);
  89. }
  90. }
  91. //普通字符串
  92. else {
  93. options.success && options.success(xhr.responseText, xhr.responseXML);
  94. }
  95. // 改变状态为完成
  96. _this.done = 1;
  97. }
  98. //如果状态码表示失败时调用错误处理回调函数
  99. else {
  100. options.error && options.error(status);
  101. // 改变状态为完成
  102. _this.done = 1;
  103. }
  104. }
  105. }
  106. },
  107. /* ====================================================================================== */
  108. hasClass: function (ele, cls) { // 判断一个类是否存在
  109. return ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'))
  110. },
  111. addClass: function (ele, cls) { // 增加一个类
  112. if (!this.hasClass(ele, cls)) ele.className = ele.className.replace(/(\s*$)/g, "") + " " + cls
  113. },
  114. removeClass: function (ele, cls) { // 删除一个类
  115. if (this.hasClass(ele, cls)) {
  116. var reg = new RegExp('(\\s|^)' + cls + '(\\s|$)');
  117. ele.className = ele.className.replace(reg, ' ')
  118. }
  119. },
  120. getStyle: function (ele, attr) { // 获取某个元素的样式
  121. var res = '';
  122. if (window.getComputedStyle) {
  123. res = getComputedStyle(ele)[attr];
  124. } else if (ele.currentStyle) {
  125. res = ele.currentStyle[attr];
  126. } else {
  127. res = ele.style[attr];
  128. }
  129. // 这里返回的样式做了调整,返回必须为字符串,有盒子的样式获取像margin-left会返回纯数字(不含单位px),这样不好处理,统一变成字符串后再对返回结果用正则匹配
  130. return res + '';
  131. },
  132. /* ====================================================================================== */
  133. show: function (ele) { // 让一个元素隐藏(display)
  134. ele.style.display = 'block'
  135. },
  136. hide: function (ele) { // 让一个元素显示(display)
  137. ele.style.display = 'none'
  138. },
  139. isShow: function (ele) { // 判断某个元素的是否显示(display,浅)
  140. var getVisibility = ele.style.display;
  141. return getVisibility !== "none";
  142. },
  143. isShowDeel: function (ele) { // 判断某个元素是否显示(display,深)
  144. temp = ele
  145. var getVisibility = this.getStyle(temp, 'display');
  146. if (getVisibility == 'none') {
  147. return false;
  148. } else if (temp.tagName == 'BODY') {
  149. return true;
  150. } else {
  151. return this.isShowDeel(temp.parentNode);
  152. }
  153. },
  154. visible: function (ele) { // 让一个元素隐藏(visible)
  155. ele.style.visibility = 'visible'
  156. },
  157. hidden: function (ele) { // 让一个元素显示(visible)
  158. ele.style.visibility = 'hidden'
  159. },
  160. /* ====================================================================================== */
  161. addElement: function (ele, tag, cls, data, inner, fn) { // 为一个元素创建一个子元素
  162. // 1、创建新元素
  163. var newEle = document.createElement(tag)
  164. // 2、设置各种属性(图片地址,自定义属性等)
  165. newEle.setAttribute('class', cls);
  166. for (var item in data) {
  167. newEle.setAttribute(item, data[item]);
  168. }
  169. // 3、设置内容(innerhtml)
  170. newEle.innerHTML = inner ? inner : ''
  171. // 4、设置回调函数
  172. fn && fn(newEle)
  173. // 5、将新元素加入到父元素中
  174. ele.appendChild(newEle)
  175. },
  176. // 帧动画(定时器容器(存在一个对象内的一个属性),图片群所在的元素,执行速度(单位:毫秒),是否只执行一次,执行完毕的回调函数)
  177. zhenAnimation: function (obj,interval, ele, speed, once, fn) {
  178. // 清除定时器
  179. clearInterval(obj[interval])
  180. // 获取图片群并全部隐藏
  181. var imgs = ele.getElementsByTagName('img')
  182. for (var i = 0; i < imgs.length; i++) {
  183. imgs[i].style.visibility = 'hidden'
  184. }
  185. // 单独显示第一张图
  186. var i = 0
  187. imgs[i].style.visibility = 'visible'
  188. // 设置定时器(其实会跳过第一张图,但感知不到)
  189. obj[interval] = setInterval(function() {
  190. // 判断是否只执行一次
  191. if (i == imgs.length-1 && once) {
  192. // 每轮执行完成的回调函数
  193. fn&&fn()
  194. clearInterval(obj[interval])
  195. return
  196. }
  197. // 前一张图隐藏、后一张图显现、可循环执行
  198. imgs[i++].style.visibility = 'hidden'
  199. if (i == imgs.length) {i=0; fn&&fn()}
  200. imgs[i].style.visibility = 'visible'
  201. }, speed)
  202. },
  203. /* ====================================================================================== */
  204. setCookie: function (name, value, t) { // 设置cookie
  205. var hour = t ? t : 8;
  206. var exp = new Date();
  207. exp.setTime(exp.getTime() + hour * 60 * 60 * 1000);
  208. document.cookie = name + "=" + escape(value) + ";expires=" + exp.toGMTString() + ";path=/";
  209. },
  210. getCookie: function (name) { // 获取cookie 存在返回值,不存在返回null
  211. var arr, reg = new RegExp("(^| )" + name + "=([^;]*)(;|$)");
  212. if (arr = document.cookie.match(reg)) {
  213. if (arr[2] != "undefined")
  214. return unescape(arr[2]);
  215. else return undefined;
  216. } else {
  217. return null;
  218. }
  219. },
  220. getStack: function (stackName) { //获取历史栈
  221. var _this = this;
  222. var stack;
  223. if (_this.getQueryString('stack')) {
  224. stack = JSON.parse(_this.getQueryString('stack'));
  225. } else if (
  226. _this.getCookie(stackName) && JSON.parse(_this.getCookie(stackName))
  227. ) {
  228. stack = JSON.parse(_this.getCookie(stackName))
  229. }
  230. // 如果cookie上没有,就把上一页路径做为返回路径
  231. else {
  232. stack = [];
  233. }
  234. return stack;
  235. },
  236. pushStack: function (stackName, url) { // 添加历史栈
  237. var _this = this;
  238. var stack;
  239. stack = _this.getStack(stackName);
  240. stack.push(url);
  241. _this.setCookie('stackName', JSON.stringify(stack));
  242. _this.setCookie(stackName, JSON.stringify(stack));
  243. },
  244. popStack: function (stackName) { // 删除历史栈
  245. var _this = this;
  246. var stack;
  247. var delTop;
  248. stack = _this.getStack(stackName);
  249. delTop = stack.pop();
  250. _this.setCookie(stackName, JSON.stringify(stack));
  251. return delTop;
  252. },
  253. /* ====================================================================================== */
  254. getQueryString: function (name) { // 获取url的某个参数
  255. var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
  256. var r = window.location.search.substr(1).match(reg);
  257. if (r != null && unescape(r[2]) != "undefined" && unescape(r[2]) != "null") return unescape(r[2]);
  258. return '';
  259. },
  260. urlParam: function (obj) { // 将对象转换为url参数字符串
  261. var _this = this;
  262. var arr = [];
  263. for (var name in obj) {
  264. arr.push(encodeURIComponent(name) + "=" + encodeURIComponent(obj[name]));
  265. }
  266. return arr.join("&");
  267. },
  268. getRandom: function (min, max) { // 获取随机数
  269. return Math.floor(Math.random() * (max - min)) + min;
  270. },
  271. arrRandom: function (arr) { // 使数组随机排序,返回一个新数组
  272. // var newArr = []
  273. // var oldArrLength = arr.length
  274. // for(var i = 0; i < oldArrLength; i++) {
  275. // newArr.push(arr.splice(getRandom(0,arr.length),1)[0])
  276. // }
  277. // return newArr
  278. arr.sort(function () {
  279. return 0.5 - Math.random()
  280. })
  281. },
  282. createArr: function (arr, num, content) { // 生成数组,
  283. // 输出举例:var arr = [{num: 5, content: {id: 1, imgurl: 'img/1.jpg'}}, {num: 2, content: {id: 2, imgurl: 'img/2.jpg'}}]
  284. // 调用举例:comm.createArr(arr, 'num', 'content')
  285. var newArr = []
  286. for (var i = 0; i < arr.length; i++) {
  287. console.log(arr[i]);
  288. console.log(arr[i][num]);
  289. for (var j = 0; j < arr[i][num]; j++) {
  290. newArr.push(arr[i][content])
  291. }
  292. }
  293. return newArr
  294. },
  295. /* ====================================================================================== */
  296. isPhone: function (phone) { // 判断是否为有效手机号
  297. if (!(/^(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\d{8}$/.test(phone))) {
  298. return false;
  299. } else {
  300. return true
  301. }
  302. },
  303. timestamp: function (time) { // 转换时间格式
  304. var sec = Math.floor(time % 60)
  305. var min = Math.floor(time / 60 % 60)
  306. var hour = Math.floor(time / 60 / 60)
  307. sec = sec < 10 ? '0' + sec : sec
  308. min = min < 10 ? '0' + min : min
  309. hour = hour < 10 ? '0' + hour : hour
  310. var timeStr = hour + ':' + min + ':' + sec;
  311. return timeStr
  312. },
  313. /* ====================================================================================== */
  314. Debuglog: function (text) { // 弹窗,用于测试
  315. if (typeof printWind == "undefined") {
  316. window.printWind = document.createElement("div");
  317. window.inner = document.createElement("div");
  318. inner.style.width = "600px";
  319. inner.style.position = "absolute";
  320. inner.style.right = "0px";
  321. printWind.appendChild(inner);
  322. printWind.style.position = "fixed";
  323. printWind.style.right = "0";
  324. printWind.style.top = "0";
  325. printWind.style.background = "rgba(0,0,0,0.6)";
  326. printWind.style.zIndex = "99999";
  327. printWind.style.padding = "20px";
  328. printWind.style.fontSize = "20px";
  329. printWind.style.width = "600px";
  330. printWind.style.height = "600px";
  331. printWind.style.color = "#0aff08";
  332. document.body.appendChild(printWind);
  333. }
  334. var para = document.createElement("p"); //创建新的<p> 元素
  335. para.innerHTML = text;
  336. para.setAttribute("class", "newLine");
  337. para.style.padding = "5px";
  338. inner.appendChild(para);
  339. var a = inner.clientHeight - 600;
  340. inner.style.top = -a + "px";
  341. },
  342. tips: function (text, time) {
  343. time = time ? time : 1500;
  344. var para = document.createElement("div"); //创建新的<p> 元素
  345. para.innerHTML = text;
  346. para.style.zIndex = 10000;
  347. para.style.borderRadius = '9px'
  348. para.style.backgroundColor = '#287AFF'
  349. para.style.textAlign = 'center'
  350. para.style.color = '#FFF'
  351. para.style.position = 'absolute'
  352. para.style.fontSize = '20px'
  353. para.style.left = '450px'
  354. para.style.top = '250px'
  355. para.style.padding = '7px 27px'
  356. para.style.width = '380px'
  357. para.style.wordBreak = 'break-all'
  358. para.style.display = 'block'
  359. document.body.appendChild(para);
  360. setTimeout(function () {
  361. document.body.removeChild(para);
  362. }, time);
  363. },
  364. /* ====================================================================================== */
  365. STBUtil: {
  366. /**
  367. * 通过键值获取机顶盒对应信息
  368. * @param name [参数名] ex:UserToken--用户token,EPGDomain--EPG域名,areaid--地区编码,templateName--当前用户模板
  369. * @return []
  370. */
  371. getSTBKey: function (name) {
  372. var value = '';
  373. if (typeof (Authentication) === "object") {
  374. if (Authentication.CTCGetConfig) {
  375. value = Authentication.CTCGetConfig(name);
  376. } else if (Authentication.CUGetConfig) {
  377. value = Authentication.CUGetConfig(name);
  378. }
  379. }
  380. return value;
  381. },
  382. /**
  383. * 通过键值设置机顶盒对应信息
  384. * @param name [参数名]
  385. * @param value [参数值]
  386. */
  387. setSTBKey: function (name, value) {
  388. if (typeof (Authentication) === "object") {
  389. Authentication.CTCSetConfig(name, value);
  390. }
  391. },
  392. /**
  393. * 获取机顶盒 用户账号/业务账号
  394. * @return {string}
  395. */
  396. getSTBUserId: function () {
  397. var userId = '';
  398. try{
  399. if (typeof (Authentication) == 'object') {
  400. // 此方法经测试目前可以获取到华为,中兴,创维三款机顶盒型号
  401. userId = Authentication.CTCGetConfig('UserID');
  402. if (!userId){
  403. userId = Authentication.CUGetConfig("UserID");
  404. }
  405. //烽火的机顶盒
  406. if(!userId){
  407. userId =Authentication.CTCGetConfig("device.userid");
  408. }
  409. //中兴老的盒子获取机顶盒型号的方法
  410. if(!userId && typeof(ztebw) == 'object' ){
  411. stbModel = ztebw.ioctlRead("infoZTEHWType");
  412. if(!stbModel) {
  413. stbModel = ztebw.ioctlRead("infoHWProduct");
  414. }
  415. }
  416. }
  417. else{
  418. console.log("不支持Authentication!");
  419. }
  420. }
  421. catch(e){
  422. userId = '';
  423. }
  424. return userId;
  425. },
  426. /**
  427. * 获取机顶盒 设备ID
  428. * @return {string}
  429. */
  430. getSTBId: function () {
  431. var stbId = "";
  432. if (typeof (Authentication) == "object") {
  433. stbId = Authentication.CTCGetConfig("STBID");
  434. if (!stbId) {
  435. stbId = Authentication.CUGetConfig("STBID");
  436. }
  437. if (!stbId) {
  438. stbId = Authentication.CTCGetConfig("device.stbid"); // 烽火的机顶盒
  439. }
  440. }
  441. // 广西广电获取stbId
  442. if (!stbId && typeof (guangxi) === "object") {
  443. stbId = guangxi.getStbNum();
  444. }
  445. return stbId;
  446. },
  447. /**
  448. * 获取机顶盒型号
  449. * @return {string}
  450. */
  451. getSTBModel: function () {
  452. var stbModel = '';
  453. try{
  454. //此方法经测试目前可以获取到华为,中兴,创维三款机顶盒型号
  455. stbModel = Authentication.CTCGetConfig('STBType');
  456. if (!stbModel){
  457. stbModel = Authentication.CUGetConfig("STBType");
  458. }
  459. //烽火的机顶盒
  460. if(!stbModel){
  461. stbModel = Authentication.CTCGetConfig("device.stbmodel");
  462. }
  463. //中兴老的盒子获取机顶盒型号的方法
  464. if(!stbModel && typeof(ztebw) == 'object' ){
  465. stbModel = ztebw.ioctlRead("infoZTEHWType");
  466. if(!stbModel){
  467. stbModel = ztebw.ioctlRead("infoHWProduct");
  468. }
  469. }
  470. }
  471. catch(e){
  472. console.log("不支持获取机顶盒型号!");
  473. }
  474. return stbModel;
  475. },
  476. /**
  477. * 获取机顶盒mac地址
  478. * @return {string}
  479. */
  480. getSTBMac: function () {
  481. if (window.isWinOS) { //Windows操作系统
  482. return "00-00-00-00-00-00";
  483. }
  484. var mac = "";
  485. if (typeof (Authentication) == "object") {
  486. try {
  487. mac = Authentication.CUGetConfig("mac");
  488. if (!mac && typeof (ztebw) == "object") {
  489. var stbId = ztebw.ioctlRead("infoHWSN");
  490. mac = stbId.substring(stbId.length - 12);
  491. }
  492. if (!mac) {
  493. mac = Authentication.CTCGetLocalMAC();
  494. }
  495. } catch (e) { }
  496. }
  497. // 广西广电获取mac
  498. if (!mac && typeof iPanel === "object") {
  499. mac = iPanel.getGlobalVar("MAC_ETH0");
  500. }
  501. // 长虹机顶盒获取mac(四川广电)
  502. if (!mac && typeof (Ethernet) === "object") {
  503. mac = Ethernet.MACAddress;
  504. }
  505. if (mac) {
  506. while (mac.indexOf(":") !== -1) {
  507. mac = mac.replace(":", "");
  508. }
  509. } else {
  510. mac = "00-00-00-00-00-00";
  511. }
  512. return mac;
  513. },
  514. /**
  515. * 获取 EPG大厅地址
  516. * @return {string}
  517. */
  518. getEPGDomain: function () {
  519. var epgDomain = "";
  520. if (typeof (Authentication) == "object") {
  521. epgDomain = Authentication.CTCGetConfig("EPGDomain");
  522. if (!epgDomain) {
  523. epgDomain = Authentication.CUGetConfig("EPGDomain");
  524. }
  525. if (typeof epgDomain === "undefined" || epgDomain == null) {
  526. epgDomain = "";
  527. }
  528. }
  529. return epgDomain;
  530. },
  531. /**
  532. * 获取 UserToken
  533. * @return {string}
  534. */
  535. getUserToken: function () {
  536. var userToken = "";
  537. if (typeof (Authentication) == "object") {
  538. userToken = Authentication.CTCGetConfig("UserToken");
  539. if (!userToken) {
  540. userToken = Authentication.CUGetConfig("UserToken");
  541. }
  542. if (!userToken) {
  543. userToken = Authentication.CTCGetConfig("device.usertoken");
  544. }
  545. }
  546. return userToken;
  547. },
  548. }
  549. };
  550. // promise插件
  551. (function (window) {
  552. // 参考Joe-Xie:https://www.cnblogs.com/XieJunBao/p/9156134.html
  553. // 原生封装promise为了应对低版本浏览器不兼容问题
  554. // 异步串行思路:promise执行then是注册回调函数,then有多个就可以注册多个回调函数,但是若多个回调都是异步执行的,那我们要等上一个异步结束后才执行下一个异步,这是时候就需要上一个异步操作完成后,把这个完成状态告诉下一个回调,这样才可以异步串行。为了解决这个问题我们把异步完成状态托管给promise去管理
  555. // 流程:第一步注册:链式调用then函数,每执行一个then函数,返回一个桥梁promise(then函数中的成功回调和失败回调是写入这个promise的回调列表中的,注意成功回调的功能除了执行本身函数外还要更新下一个promise的状态)
  556. // 第二步执行:第一个promise的异步执行完,开始执行第一个promise的回调函数(回调函数又分两步走:第一步:resolvePromise解析回调返回值(如果是promise则说明是异步,就需要继续解析直到不是promise而是一个具体的值),第二步:当回调返回的值是一个具体值而不是promise时,调用第二个proomise的reslove方法将第二个proomise的状态更新为fulfilled,并将第一个promise的回调的值传入p2的回调函数中去执行)
  557. function MyPromise(fn) {
  558. var self = this;
  559. // 成功回调传的参数
  560. self.value = null;
  561. // 失败回调传的参数
  562. self.error = null;
  563. // 当前promise对象的状态
  564. self.status = "pending";
  565. // 存储成功回调列表
  566. self.onFulfilledCallbacks = [];
  567. // 存储失败回调列表
  568. self.onRejectedCallbacks = [];
  569. // 状态改变并执行回调
  570. // 成功
  571. function resolve(value) {
  572. // 判断传入参数是否由MyPromise构造的对象,若是,注册该函数
  573. if (value instanceof MyPromise) {
  574. return value.then(resolve, reject);
  575. }
  576. // 判断
  577. if (self.status === "pending") {
  578. setTimeout(function () {
  579. self.status = "fulfilled";
  580. self.value = value;
  581. // 执行成功回调
  582. // self.onFulfilledCallbacks.forEach(function(callback){callback(self.value)});
  583. // 向下兼容forEach
  584. for (var i = 0; i < self.onFulfilledCallbacks.length; i++) {
  585. self.onFulfilledCallbacks[i](self.value);
  586. }
  587. }, 0)
  588. }
  589. }
  590. // 失败
  591. function reject(error) {
  592. if (self.status === "pending") {
  593. setTimeout(function () {
  594. self.status = "rejected";
  595. self.error = error;
  596. // self.onRejectedCallbacks.forEach(function(callback){callback(self.error)});
  597. for (var i = 0; i < self.onRejectedCallbacks.length; i++) {
  598. self.onRejectedCallbacks[i](self.error);
  599. }
  600. }, 0)
  601. }
  602. }
  603. try {
  604. fn(resolve, reject);
  605. }
  606. catch (e) {
  607. reject(e);
  608. }
  609. }
  610. // 解析放回值
  611. // 用来解析回调函数的返回值x,x可能是普通值也可能是个promise对象
  612. // 因为回调函数既可能会返回一个异步的promise也可能会返回一个同步结果,所以我们把直接把回调函数的结果托管给bridgePromise,使用resolvePromise方法来解析回调函数的结果,如果回调函数返回一个promise并且状态还是pending,就在这个promise的then方法中继续解析这个promise reslove传过来的值,如果值还是pending状态的promise就继续解析,直到不是一个异步promise,而是一个正常值就使用bridgePromise的reslove方法将bridgePromise的状态改为fulfilled,并调用onFulfilledCallbacks回调数组中的方法,将该值传入,到此异步操作就衔接上了。
  613. function resolvePromise(bridgepromise, x, resolve, reject) {
  614. // bridgepromise是桥梁promise,x是桥梁promise中注册的成功回调的返回值,resolve和reject是桥梁promise的状态改变函数
  615. // 2.3.1规范,避免循环引用
  616. // 如果成功回调的值又是桥梁promise就返回循环传参的错误(死循环)
  617. if (bridgepromise === x) {
  618. return reject(new TypeError('Circular reference'));
  619. }
  620. var called = false;
  621. // // 如果x是一个promise,则通过递归
  622. // if (x instanceof MyPromise) {
  623. // //如果这个promise是pending状态,就在它的then方法里继续执行resolvePromise解析它的结果,直到返回值不是一个pending状态的promise为止(这里使用了递归的方法)
  624. // if (x.status === "pending") {
  625. // x.then(
  626. // function(y){
  627. // resolvePromise(bridgepromise, y, resolve, reject);
  628. // },
  629. // function(error){
  630. // reject(error);
  631. // }
  632. // );
  633. // }
  634. // else {
  635. // x.then(resolve, reject);
  636. // }
  637. // }
  638. // else
  639. // // 如果x是一个promise,则继续解析它的状态
  640. if (x != null && ((typeof x === 'object') || (typeof x === 'function'))) {
  641. try {
  642. var then = x.then;
  643. if (typeof then === 'function') {
  644. // then方法的指向传入的桥梁promise,也就是说该桥梁promise调用了then方法并传入了成功回调和失败回调
  645. then.call(
  646. x,
  647. // 传入then的成功回调
  648. function (y) {
  649. if (called) return;
  650. called = true;
  651. // 这里重新解析当前的桥梁promise,至于成功回调的返回值传空(这里目的是通过递归持续判断当前桥梁promise的状态)
  652. resolvePromise(bridgepromise, y, resolve, reject);
  653. },
  654. //传入then的失败回调
  655. function (error) {
  656. if (called) return;
  657. called = true;
  658. reject(error);
  659. }
  660. )
  661. }
  662. // 如果then不是一个函数,则以x为值改变promise状态并延长成功回调列表
  663. else {
  664. resolve(x);
  665. }
  666. }
  667. // 如果在取x.then值时抛出了异常,则以这个异常做为原因将promise拒绝。
  668. catch (e) {
  669. if (called) return;
  670. called = true;
  671. reject(e);
  672. }
  673. }
  674. // 如过x不是一个promise,则改变bridgePromise的状态改为fulfilled,并调用onFulfilledCallbacks回调数组中的方法,将该值传入
  675. else {
  676. resolve(x);
  677. }
  678. }
  679. // 注册回调函数
  680. MyPromise.prototype.then = function (onFulfilled, onRejected) {
  681. var self = this;
  682. // 搭建桥梁promise(即调用为then方法后重新返回一个新的promise对象)
  683. var bridgePromise;
  684. // 防止使用者不传成功或失败回调函数,所以成功失败回调都给了默认回调函数
  685. onFulfilled = typeof onFulfilled === "function" ? onFulfilled : function (value) { return value };
  686. onRejected = typeof onRejected === "function" ? onRejected : function (error) { throw error };
  687. // 如果当前的promise对象是完成状态
  688. // 返回一个新的桥梁promise
  689. if (self.status === "fulfilled") {
  690. return bridgePromise = new MyPromise(function (resolve, reject) {
  691. setTimeout(function () {
  692. try {
  693. // 获取成功回调函数的返回值
  694. var x = onFulfilled(self.value);
  695. // 解析桥梁promise函数
  696. resolvePromise(bridgePromise, x, resolve, reject);
  697. }
  698. catch (e) {
  699. reject(e);
  700. }
  701. }, 0);
  702. })
  703. }
  704. // 如果当前的promise对象是拒绝状态
  705. if (self.status === "rejected") {
  706. return bridgePromise = new MyPromise(function (resolve, reject) {
  707. setTimeout(function () {
  708. try {
  709. var x = onRejected(self.error);
  710. resolvePromise(bridgePromise, x, resolve, reject);
  711. }
  712. catch (e) {
  713. reject(e);
  714. }
  715. }, 0);
  716. });
  717. }
  718. // 如果当前的promise对象是听候状态,则在当前promise对象的成功回调列表和失败回调列表中注入
  719. if (self.status === "pending") {
  720. return bridgePromise = new MyPromise(function (resolve, reject) {
  721. // 注意回调列表是把整个回调函数和回调解析函数一起注入的!!!!!,所以在执行回调时除运行回调函数还要,解析桥梁promise的状态(有可能桥梁promise中也有promise),解析中改变当前promise的状态,若当前promise的状态为完成状态才继续执行下一个注册好的回调
  722. self.onFulfilledCallbacks.push(function (value) {
  723. try {
  724. var x = onFulfilled(value);
  725. resolvePromise(bridgePromise, x, resolve, reject);
  726. }
  727. catch (e) {
  728. reject(e);
  729. }
  730. });
  731. self.onRejectedCallbacks.push(function (error) {
  732. try {
  733. var x = onRejected(error);
  734. resolvePromise(bridgePromise, x, resolve, reject);
  735. }
  736. catch (e) {
  737. reject(e);
  738. }
  739. });
  740. });
  741. }
  742. }
  743. MyPromise.prototype.MyCatch = function (onRejected) {
  744. return this.then(null, onRejected);
  745. }
  746. MyPromise.all = function (promises) {
  747. return new MyPromise(function (resolve, reject) {
  748. var result = [];
  749. var count = 0;
  750. for (var i = 0; i < promises.length; i++) {
  751. // (function(i){
  752. // return promises[i].then(function(data) {
  753. // result[i] = data;
  754. // if (++count == promises.length) {
  755. // resolve(result);
  756. // }
  757. // }, function(error) {
  758. // reject(error);
  759. // });
  760. // })(i)
  761. function closeTemp(i) {
  762. return promises[i].then(function (data) {
  763. result[i] = data;
  764. if (++count == promises.length) {
  765. resolve(result);
  766. }
  767. }, function (error) {
  768. reject(error);
  769. });
  770. }
  771. closeTemp(i)
  772. }
  773. });
  774. }
  775. MyPromise.race = function (promises) {
  776. return new MyPromise(function (resolve, reject) {
  777. for (var i = 0; i < promises.length; i++) {
  778. promises[i].then(function (data) {
  779. resolve(data);
  780. }, function (error) {
  781. reject(error);
  782. });
  783. }
  784. });
  785. }
  786. MyPromise.resolve = function (value) {
  787. return new MyPromise(function (resolve) {
  788. resolve(value);
  789. });
  790. }
  791. MyPromise.reject = function (error) {
  792. return new MyPromise(function (resolve, reject) {
  793. reject(error);
  794. });
  795. }
  796. MyPromise.promisify = function (fn) {
  797. return function () {
  798. var args = Array.from(arguments);
  799. return new MyPromise(function (resolve, reject) {
  800. fn.apply(null, args.concat(function (err) {
  801. err ? reject(err) : resolve(arguments[1])
  802. }));
  803. })
  804. }
  805. }
  806. window.MyPromise = MyPromise;
  807. })(window);