深入浅出JS:Two
JS中的Promise:
MDN上面对promise的描述:Promise 对象用于表示一个异步操作的最终状态(完成或失败),以及其返回的值。
可以直接对字面上理解:Promise:承诺,一诺千金,只要你有承诺就得执行,不管时间过了多久,执行完就行,而且还是异步的,比如你是一个言出必行的大丈夫,你说总有一天完会把大黄蜂(科迈罗)买回家,奋斗了5年攒够了钱,然后下单,异步就是你承诺了要买大黄蜂,但你还是可以做其他的事,因为生活总得继续。
console.log('下个目标,5年买科迈罗!');
new Promise((resolve, reject) => {
console.log('正在奋斗中。。。。');
//你可以执行的代码
//经过了5年的奋斗,我们用延迟函数代替代码执行的时间,3秒
setTimeout(() => {
console.log('奋斗了5年,并攒够了钱!');
resolve('下单');
},3000)
}).then(value => {
console.log(`${value},带着女神出去浪`);
})
console.log('一边享受生活一边奋斗。。。。');
运行结果:
下个目标,5年买科迈罗!
正在奋斗中。。。。
一边享受生活一边奋斗。。。。
奋斗了5年,并攒够了钱!
下单,带着女神出去浪
这是最简单的Promise了,接下来我们正式介绍一下Promise:MDN描述:Promise
对象是一个代理对象(代理一个值),被代理的值在Promise对象创建时可能是未知的。它允许你为异步操作的成功和失败分别绑定相应的处理方法(handlers)。 这让异步方法可以像同步方法那样返回值,但并不是立即返回最终执行结果,而是一个能代表未来出现的结果的promise对象。
简单来说Promise里面可以执行一些获取数据库数据的代码,因为获取时间比较长,因为JS是单线程的,不能因为获取数据让其他事件不能加载。
一个 Promise
有以下几种状态:
- pending: 初始状态,既不是成功,也不是失败状态。
- fulfilled: 意味着操作成功完成。
- rejected: 意味着操作失败。
返回看上面那个例子,一开始有两个参数resolve => 成功, reject => 失败.还有个重点:Promise状态发生改变,就会触发.then()里的响应函数,处理后续步骤.
console.log('start');
new Promise((resolve, reject) => {
console.log('first promise');
resolve('succeed');
setTimeout(() => {
console.log('最后才执行到我!');
},2000)
}).then(value => {
console.log(`${value},first then`);
})
运行结果:
start
first promise
succeed,first then
最后才执行到我!
上面那个例子,执行到resolve(),当前的promise的状态改变了,就开始执行下面的then,不会等待settimeout执行完。
结合下面的图可以理解下:
当然promise还可以当作一个变量来对待,比如下面的例子,不一定要紧接着()后面.then();
console.log('start'); let promise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('the promise fulfilled');
resolve('hello,world');
},1000);
}); //上面的promise完成后,过3秒再执行then,还是可以存在的,可以作为一个变量。
setTimeout(() => {
promise.then(value => {
console.log(value);
})
},3000)
接下来来说说.then(),经过上面的理解,可以看出.then()相当于一个promise,它也返回一个新的Promise实例,所以它可以链式调用,接受两个参数,也就是Promise中的两个状态:fulfilled,rejected,当前面的Promise状态改变时,.then()根据其最终状态,选择特定的状态响应函数执行。
'use strict'; console.log('that\'s begin');
let myPromise = new Promise((resolve, reject) => {
console.log('first promise');
//resolve是传递参数给下一个then
resolve('first succeed');
}).then( value => {
console.log('value');
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('thie is error');
},2000)
});
//当上面出错时调用下面的catch
}).catch((reason) => {
console.log(reason);
})
再看个返回失败的例子,返回成功时用resolve,后面就用.then()来处理后续步骤,返回失败时用rejected,后面就用.catch()来处理后续步骤。
console.log('that\'s begin');
let myPromise = new Promise((resolve, reject) => {
console.log('first promise');
//resolve是传递参数给下一个then
resolve('first succeed');
}).then( value => {
console.log('first then succeed');
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('thie is error');
},2000)
});
//当上面出错时调用下面的catch
}).then( value => {
console.log(`成功执行到最后:${value}`);
})
.catch( reason => {
console.log(`最终以失败告终:${reason}`);
})
运行结果:
that's begin
first promise
succeed
最终以失败告终:thie is error
下面讲一个Promise的all函数:Promise.all(iterable)
方法返回一个 Promise
实例,此实例在 iterable
参数内所有的 promise
都“完成(resolved)”或参数中不包含 promise
时回调完成(resolve);如果参数中 promise
有一个失败(rejected),此实例回调失败(rejecte),失败原因的是第一个失败 promise
的结果,MDN讲的很清晰,直接看例子:
console.log('start all promise');
let promise1 = Promise.reject('我是第一个reject');
let promise2 = 42;
let promise3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('我是第二个reject');
},1000);
});
Promise.all([promise1, promise2, promise3]).then( value =>{
console.log(value);
}).catch(reason => {
console.log(`失败原因的是第一个失败 promise 的结果:${reason}`);
})
运行结果:
start all promise
失败原因的是第一个失败 promise 的结果:我是第一个reject
如果都是resolve的话才算成功,如果前面的promise没有返回resolve或者没有返回reject那么下面的then()就不会执行。
JS中的跨域
请求跨域有好多种,
一、跨域资源共享:
也就是设置服务端的header,可以指定哪些域名可以请求,也是最简单的跨域方式(自我感觉),并且可以支持post等等。
//指定允许其他域名访问
'Access-Control-Allow-Origin:*'//或指定域
//响应类型
'Access-Control-Allow-Methods:GET,POST'
//响应头设置
'Access-Control-Allow-Headers:x-requested-with,content-type'
二、jsonp
由于浏览器的同源策略,所以ajax不能直接跨域请求数据,我们把浏览器的安全策略组关掉(也就是关掉同源策略)就可以啦,当然这是开个玩笑。想了解可以访问这个网站:https://www.cnblogs.com/zhongxia/p/5416024.html
上面说的是ajax会被同源策略拦截下来,但是script中的src并不会被拦截,我们可以利用这个特性实现跨域:
<script>
//下面就输出你在服务端请求的数据
function dosomething(data){
console.log(data);
}
</script>
//src 就是你想要请求的地址,cb是请求时带的参数,这是必须要加的,因为上面的函数名也是这个
<script src="http://localhost:8080/ChickensSys/backDate?cb=dosomething"></script>
服务端要做相应的配合,这边是用javaweb来调试:在doGet函数里面添加这些代码:
response.setContentType("text/html");
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
String cbString = request.getParameter("cb");
PrintWriter out = response.getWriter();
out.println(cbString + "("this is jsonP ")");
out.flush();
out.close();
仔细看:out.println(cbString + "("this is jsonP ")");
我们把参数结果带入进去,可以发现输出的是cb("this is jsonP");相当于是调用这个函数,没错,结果就是这样。
你会看到页面的控制台打印了this isjsonP。
结论:我们用src(不会被同源策略拦截)请求别的域名的数据,返回的结果放在已定义好的函数参数里面并且执行这个函数,然后你要对数据做哪些操作就可以在函数里面随便操作。
三、window.name + iframe跨域获取数据
这个的原理和jsonP差不多吧,也是利用src,不过这次是利用iframe(可以理解html中一个内嵌页面,也可以包含一个html文件)的src和window.name的特性来跨域, window.name属性的神奇之处在于name 值在不同的页面(甚至不同域名)加载后依旧存在(如果没修改则值不会变化),并且可以支持非常长的 name 值(2MB)。也就是你第一次设置后,不管你的iframe重定向到哪个页面,window.name都不会变。
<script type="text/javascript">
let iframe = document.createElement('iframe');
//src你请求的地址,此时会报错,说你跨域了
iframe.src = 'http://localhost:8080/ChickensSys/backDate';
document.body.appendChild(iframe);
iframe.onload = function() {
//在本地新建空白的proxy.html因为上面报错了,这边赶紧重定向到本地的proxy.html
iframe.src = 'proxy.html';
//contentWindow属性是指指定的frame或者iframe所在的window对象。
console.log(iframe.contentWindow.name)
};
</script>
服务端需要相应的配合:
out.println("<script>window.name = \"this is CORS</script>");
预期结果跟我们想的一样,一开始报错,我们重定向到本地后就没报错,输出结果也是没错的,因为服务端输出的是一个脚本,window.name=" ";还记得上面讲过window.name 值在不同的页面(甚至不同域名)加载后依旧存在(如果没修改则值不会变化)。但是为什么会一直调用onload函数呢,因为在onload函数中又被重定向所以又一次调用,跟递归差不多意思。既然可以拿到数据,那我们下面来改进下,让它不报错,并只拿到一次数据。
<script>
let iframe = document.createElement('iframe');
iframe.style.display = 'none';
var state = 0;
iframe.onload = function() {
//重定向完,可以取window.name的数据了,这边可以对数据进行处理
if(state === 1) {
var data = iframe.contentWindow.name;
console.log(data);
//移除这个节点,其实上面display:none已经对dom无影响了,这个加了保险
document.body.removeChild(iframe);
//当下面的iframe.src执行到时,也就是第一次onload,迅速重定向到本地
} else if(state === 0) {
state = 1;
iframe.src = 'proxy.html';
}
};
iframe.src = 'http://localhost:8080/ChickensSys/backDate';
document.body.appendChild(iframe);
</script>
运行结果:
只有一次请求。
上面只是讲下原理,我们可以对上面做个封装,封装成一个函数,方便以后用:
<script>
//url是请求的地址,handle是处理数据的函数
function getDataWithIframe(url, handle){
let iframe = document.createElement('iframe');
iframe.style.display = 'none';
var state = 0;
iframe.onload = function() {
//重定向完,可以取window.name的数据了,这边可以对数据进行处理
if(state === 1) {
handle(iframe.contentWindow.name);
//移除这个节点,其实上面display:none已经对dom无影响了,这个加了保险
document.body.removeChild(iframe);
//当下面的iframe.src执行到时,也就是第一次onload,迅速重定向到本地
} else if(state === 0) {
state = 1;
iframe.src = 'proxy.html';
}
};
iframe.src = url;
document.body.appendChild(iframe);
}
let url = 'http://localhost:8080/ChickensSys/backDate';
getDataWithIframe(url, function(data){
console.log('这是请求来的数据:' + data);
})
</script>
总结:看了好多跨域,大部分都是通过src来跨域的,但好像通过src的都只能是get请求,比如刚说的jsonP和iframe+window.name的方式都是通过get来请求数据,而且都是需要要后端进行相应的配合,比如jsonP需要加个cb()来执行函数,iframe需要<script>window.name=" "</script>来执行赋值语句,第一种设置后端的header的方式倒是可以支持post请求,真是又简单又好用。。。
深入浅出JS:Two的更多相关文章
- 深入浅出js事件
深入浅出js事件 一.事件流 事件冒泡和事件捕获分别由微软和网景公司提出,这两个概念是为了解决页面中事件流(事件发生顺序)的问题. <div id="outer"> & ...
- 深入浅出JS的封装与继承
JS虽然是一个面向对象的语言,但是不是典型的面向对象语言.Java/C++的面向对象是object - class的关系,而JS是object - object的关系,中间通过原型prototype连 ...
- 深入浅出js实现继承的7种方式
给大家介绍7中js继承的方法 有些人认为JavaScript并不是真正的面向对象语言,在经典的面向对象语言中,您可能倾向于定义类对象,然后您可以简单地定义哪些类继承哪些类(参考C++ inherita ...
- 深入浅出js中的this(一)
Q:this是什么? A:this是Javascript语言的一个关键字,它代表函数运行时,自动生成的一个内部对象,在每个 function 中自动根据作用域(scope) 确定, 指向的是此次调用者 ...
- 深入浅出js中的this
Q:this是什么? A:this是Javascript语言的一个关键字,它代表函数运行时,自动生成的一个内部对象,在每个 function 中自动根据作用域(scope) 确定, 指向的是此次调用者 ...
- 《JS高程》事件学习笔记
事件:文档或浏览器窗口中发生的一些特定的交互瞬间,也即用户或浏览器自身执行的某种动作. -------------------------------------------------------- ...
- JS:事件循环机制、调用栈以及任务队列
点击查看原文 写在前面 js里的事件循环机制十分有趣.从很多面试题也可以看出来,考察简单的setTimeout也就是考察这个机制的. 在之前,我只是简单地认为由于函数执行很快,setTimeout执行 ...
- web前端知识总结
前言: 一直想着整理一下关于前端的知识体系和资料,工作忙了些,挤挤总会有的,资料很多,就看你能不能耐下心坚持去学了,要多学多敲多想,祝你进步~ 学习之前首先要大概了解什么是HTML ,CSS , JS ...
- 介绍Ext JS 4.2的新特性的《深入浅出Ext JS》上市
以用户为中心的时代,应用的界面外观变得越来越重要.然而,很多程序员都缺乏美术功底,要开发出界面美观的应用实属不易.Ext JS的出现,为广大程序员解决了这一难题.它有丰富多彩的界面和强大的功能,是开发 ...
随机推荐
- Python删除列表中的空格
list1 = ['122','2333','3444',' ','422',' ',' ','54',' '] list1=[x.strip() for x in list1 if x.strip( ...
- 我的scoi2018
高一,很尴尬,凉~ -------- 大家好,我是分界线,我弱弱的说本次采用倒序的写作手法 -------- 故事是这样讲的: Day0: 刚刚去那个电科搞的集训,早上才考了一波模拟赛,下午就过来住酒 ...
- 2、eureka注册中心集群
1. Eureka作为spring cloud的服务发现与注册中心,在整个的微服务体系中,处于核心位置.单机模式下的eureka服务,显然不能满足高可用的实际生产环境,这就要求配置一个能够应对各种突发 ...
- vue $attrs 父组件和孙子组件的传值,传函数
上一篇我们说到provide/inject的依赖注入的传值方法,今天我们来说一下另一个父组件给孙子组件的传值方式$attrs 我们接着上一个例子继续来写 parent父组件 我们给child子组件穿了 ...
- 为 STM32 移植 Berry 脚本语言
Berry 是我为单片机设计的一款脚本语言,该语言具有资源占用小.平台无关.执行速度快和易于掌握等优点.在单片机上使用脚本语言可以提高单片机的二次开发能力以及调试效率,同时也是一种比较新颖的玩法.本教 ...
- compiz隐藏最大化窗口标题栏
xfwm换了compiz试试,还行,挺方便.就是这个隐藏最大化窗口的标题栏没有现成的ui设置项,google到如下解决方案: 修改后立即生效. https://planetkris.com/2009/ ...
- win10居然把Linux的引导覆盖了
昨天晚上装了个windows10系统试了试,发现触摸板真的难用.最基本的双指点击做右键都搞不出来,开始菜单里要上下滚动的时候触摸板竟然要水平滑动-- 重启的时候发现居然没有Linux的引导选项了 试了 ...
- Android应用图标尺寸规范(转)
转自:http://blog.sina.com.cn/s/blog_4b20ae2e0101h84o.html Android Icon Size and Location for Apps DENS ...
- Java中static关键字的定义
1.static存在的主要意义 static的主要意义是在于创建独立于具体对象的域变量或者方法.以致于即使没有创建对象,也能使用属性和调用方法! static关键字还有一个比较关键的作用就是 用来形成 ...
- 小程序入坑(一)---如何引入iconfont 字体图标
最近一直忙于日常任务,其实是懒癌又犯了..........不过因为自己的“懒癌”,“不思进取”给自己挖了不少坑. 一,小程序工具的安装 打开简易小程序的官网https://mp.weixin.qq.c ...