Promise
Promise的产生背景—回调函数噩梦
回调函数噩梦(恐怖回调),也被称为恶魔金字塔,指如ajax依赖调用时,回调函数会层层嵌套,而这种层层嵌套的写法,往往会让人难以理解,所以称之为噩梦。
缺点:
- 结构混乱
例如:服务器中有3个txt文件,我们需要在html上,通过js中的异步的ajax,分别获取这3个文件的内容,假设这3个文件分别存储的数据为1、2、3,那么我希望在js中,能够求出1+2+3,把6输出。
你觉得代码应该怎么写?
var str = "";
$.ajax({
type: "get",
url: "../data/1.txt",
success: function (msg1) {
console.log(msg1);
str += msg1;
setTimeout(function () {
$.ajax({
type: "get",
url: "../data/2.txt",
success: function (msg2) {
console.log(msg2);
str += msg2;
$.ajax({
type: "get",
url: "../data/3.txt",
success: function (msg3) {
console.log(msg3);
str += msg3;
console.log(str);
}
})
}
})
}, 10)
}
})
Promise对象
Promise是一种思维方式,是一种解决回调函数噩梦的解决方案(Promise 是异步编程的一种解决方案)。
所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作,也可以放同步的)的结果。
Promise构造函数
new Promise(fn)=>返回的值promise对象(实例) 状态pending(进行中)
Promise对象代表一个异步操作,有三种状态:
- Pending(进行中,异步操作没有得到结果)
- Resolved(已完成,又称Fulfilled)
- Rejected(已失败)
注:可以把Promise看成是状态机,当该Promise对象创建出来之后,其状态就是进行中,然后通过程序来控制到底是执行已完成,还是执行已失败。因为Promise处理的是异步任务,所以我们还得对Promise做监听,当Promise的状态发生变化时,我们要执行相应的函数。
Promise的特点
-
对象的状态不受外界影响 (promise的回调函数中的代码不会影响p的状态)
- 想要改变状态 需要借助 resolve,rejected 方法,由自己决定
- resolve() => pending(进行中状态) => fulfilled(已成功)
- reject() => pending(进行中状态) => rejected(已失败)
注意:resolve,reject两个方法除了可以改变状态,还可以传递一些数据
- 想要改变状态 需要借助 resolve,rejected 方法,由自己决定
-
Promise 对象 的状态一经改变 不能再次改变
Promise的优点:
优点 有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数
缺点
- 无法取消Promise,一旦新建它就会立即执行,无法中途取消。
- 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。(直接报错)
- 当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)
promise的相关方法
then
then方法接收两个回调函数 (提前决定 要执行的操作)
-
当promise实例(p)的状态,由pending(进行中)改为fullfilledI(已成功)执行第一个回调函数,说明调用了resolve方法()
-
当promise实例(p)的状态,由pending(进行中)改为rejected(已失败)执行第二个回调函数,说明调用了reject方法()
先规定,再调用
- onResolved函数: 成功的回调函数 (value) => {}
- onRejected函数: 失败的回调函数 (reason) => {}
- 说明: 指定用于得到成功value的成功回调和用于得到失败reason的失败回调
- then方法
- 如果请求成功调用完毕之后,会默认返回一个新的promise对象(实例),默认fulfilled(以成功的状态)
- 如果回调函数的return值, 是一个非promise对象,即是常规类型(7大类型),则将返回值,处理之后生成新的promise实例
- 如果回调函数的return值,返回一个promsie对象,则直接生成,返回一个新的promise对象(数值相同,但引用空间不同,不不全等)
- 如果报错,此处没有接收,错误,直接返回 失败(rejected)状态的实例
- 如果请求成功调用完毕之后,会默认返回一个新的promise对象(实例),默认fulfilled(以成功的状态)
可以形成链式操作
链式操作
then方法最终返回的是promise实例
// 获取元素
var userInp = document.getElementById('user');
var btn = document.getElementById('btn');
btn.onclick = function () {
var user = userInp.value.trim();
var p = isExistUser(user);
// 提前规定好成功、失败返回的结果
p.then(function (res) {
console.log(res);
}, function (res) {
console.log(res)
})
}
// 函数封装
function isExistUser(user) {
var p = new Promise(function (resolve, reject) {
$.ajax({
type:"get",
url: "../php/isExist.php",
data:{
user : user
},
dataType : "json",
success(result){
console.log(result); //请求的结果
if (result.status) { // 用户不存在 可以注册
resolve("可以注册"); //"可以注册" => 传递的数据
} else { //用户已存在
reject("用户存在");
}
// console.log("函数中的p", p);
}
})
});
return p;
}
catch
普通抛出错误
对于Promise函数中出现的错误(2种)=> 默认变成rejected
-
执行时语法错误,会被函数内部捕获,此时p状态 默认改为rejected(已失败),并把错误信息作为数据,传递出去reject(err);
拆开写
合写
- reject()调用时抛出的错误
可以进行捕获,捕获方法如下
-
第一种 通过then方法中的第二个回调函数进行捕获
- 出现错误后,默认变成rejected,被函数内部捕获,放到then方法中的第二个回调函数(reject)
-
第二种 通过catch方法捕获,对于成功状态就没有任何操作了了
then方法,调用完毕之后,会默认返回一个新的promise对象
catch运用
promsie的嵌套
promise的静态方法
then和catch是给对象用的方法
Promise.all([])
接收一个数组(存放多个promise实例(对象)),将多个Promise实例,包装成一个新的Promise实例
和请求顺序无关,和在数组中的顺序有关,同时发送请求,按最慢的来,等所有请求成功之后,才会执行后面的then,返回数据的集合(数组)
新的Promise实例才为fulfilled状态(已成功) => 返回(p1,p2,p3)实例形成的数组
说明:
- 返回一个新的promise,只有当所有的promise都成功(都为fulfilled状态)才成功
- 只要有一个失败了(rejected),就直接失败,立即停止,错误结果被catch捕获
promise.resolve()
接收一个参数,返回对应值的promise对象(fulfilled)
promise.reject()
接收一个参数,返回对应值的promise对象(rejected)
Promise.race()
promise.race 只要实例(p1,p2,p3)中有、有一个状态发生改变(成功|失败),新的Pronise实例,也会跟着改变,按最快的那个来
接收一个数组(存放多个promise实例(对象)),将多个Promise实例,包装成一个新的Promise实例
Promise.allSettled()
按最慢的来 => 等所有请求都成功之后 => 统一返回一个数据
接收一个数组(存放多个promise实例(对象)),将多个Promise实例,包装成一个新的Promise实例
-
不管结果是
fulfilled
,还是rejected
,成功和失败全都返回,放到一个数组中- 成功,则返回一个对象,对象包含status和value
- 失败,则返回一个对象,对象包含status和reason
var promise = new Promise( function( resolve, reject ){
// 通常这里写一个异步任务,在这个异步任务中,
// 通过resolve或reject来改变promise的状态。
resolve(“数据”);
// resolve指将状态更改为已完成
// reject指将状态更改为已失败。
});
// then实际上是对promise的状态监听
// 当状态为已完成时,触发第一个参数函数。
// 当状态为已失败时,触发第二个参数函数(第二个参数函数,可写可不写)
// 每一个函数中的参数,指当初状态改变时,所保存的数据。
promise.then(function(str){}, function(err){});
// or
// catch()方法是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数。(注 Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。)
promise.then(function(str){}).catch(function(err){})
all:每一个promise都执行后,再去执行then。
Promise.all( [ promise1, promise2… ] ).then( function( result ){} )
race:只要有一个promise执行后,就去执行then。
Promise.race( [ promise1, promise2… ] ).then( function( result ){} )
函数封装
先 同步得到一个进行中状态
then提前决定状态改变要执行的操作,要执行的操作
先决定执行说明内容,等未来结果过来之后,再决定做什么操作
跨域
同源策略
- 同源策略:同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能
- 当一个源(网站)访问另一个源(网站)的服务器(后端),浏览器出于安全考虑默认拒绝本次访问
由客户端访问某一个服务器的文件时,如果发起请求的这个来源与进行响应的目标的源不一致,目标源出于安全的角度,将拒绝这次访问。
同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。
比如,我们网站想从别人网站中拿一些数据,那么默认的时候,别人网站是不会给我们数据的。
协议名、域名、端口,这三项都相同,则被称为同源
三项中只要有一项不同,则被称为跨源/跨域。
协议名指http、https等这种
域名包含网站域名、ip地址、计算机名称
端口默认是80,80并不会显示在浏览器地址栏中
# 下面哪组网址跨源(跨域)了?
http://127.0.0.1/1.html http://127.0.0.1/2.html
http://127.0.0.1:80/1.html http://127.0.0.1
http://127.0.0.1/ http://localhost/
http://localhost:81/1.html http://localhost:82/1.html
// 二级域名和一级域名,也需要跨域
http://abc.com/ http://mp3.abc.com/
http://mp3.abc.com/ https://mp3.abc.com/
跨源访问也叫做跨域访问。
真实开发中,很多场景下会涉及到跨域访问,比如百度网站就特别大,其中有mp3.baidu.com、image.baidu.com、tieba.baidu.com等等,而其中一些数据或服务是需要共用的,所以就需要跨域访问。
再比如支付系统,各个电商网站都提供支付宝或微信付款,这个就需要用到支付宝或微信的接口,所以这也是跨域访问。
那么,如何进行跨域访问呢?
跨域访问
AJAX解决的是数据交互问题,即把数据传递给对方,把对方的数据拿回来。
但有时对方网站因同源策略的限制,拒绝我们的请求,不给我们数据,所以我们需要想办法解决这个问题。
解决跨域访问的方法:
- 对方服务器CORS(跨域资源共享 )
指在对方服务器上做配置,即让他们允许我们访问,这样我们再通过AJAX就可以拿到数据了。
- 我方客户端JSONP(原理 引入资源不受同源策略的限制 link的href script的src img的src )
JSONP和AJAX一样,也是一种数据交互的方式。
无论对方网站是否允许我们访问,我们直接通过script标签,访问对方文件,拿到对方数据。
AJAX和JSONP都可以做数据交互,当有这种需求时,我们应该在AJAX和JSONP中选择一种。
-
如果拿回来的响应内容,被一个函数包裹着,可以用JSONP处理。
-
如果拿回来的响应内容,没有被函数包裹,可以用AJAX处理。
跨域访问之CORS
CORS指跨域资源共享
对方服务器配置CORS
<?php
// 下面这句话指允许来至任何人的访问
header('Access-Control-Allow-Origin: *');
// 允许特定的网址来访问,配置多个,最后一条生效
header(‘Access-Control-Allow-Origin:http://127.0.0.1:8020’);
?>
跨域访问之JSONP
script的src 用来引入资源不受跨域的影响),也是需要对方后端配合的
注意JSONP
不是ajax,在network的js中
引入资源不受跨域的限制(link img script)
也是需要后端配合的
优点:
- 请求速度比ajax比较快
- 代码简洁
缺点:
- 也是需要对方配合
- 只能发生get请求,不能使用post,不安全(只能做一些简单的数据传输)
因为script标签支持跨域访问,所以我们可以创建script标签,src指向一个跨域文件。
那么,该跨域文件的响应内容,就变成script标签内的内容了。
所以,该内容也可以被理解成是正在被执行的js代码。
如果该代码中有一个正在被执行的函数,该函数中又有参数。
我们就可以把这个参数理解成服务器传给客户端的数据。
客户端去定义这个函数,我们就能够对参数做任意处理了,callback只是接受数据
php返回的文本 拼接var
以下这种方法用的很少
把请求的php(返回的是文本),放到js中,加上var,拼接成字符串json
把php接口,放到script标签里,此时就变成了js
php返回的文本 拼接函数(在html中通过script引数据,不走ajax)
将callback函数式名抽离为参数
此时callback的名字就要改成cb
在html中通过script引数据
服务器代理
服务器(后端) => 服务器(后端)
try…catch…finally
try/catch/finally 语句用于处理代码中可能出现的错误信息。
错误可能是语法错误,通常是程序员造成的编码错误或错别字。也 可能是拼写错误或语言中缺少的功能(可能由于浏览器差异)。
try语句允许我们定义在执行时进行错误测试的代码块。
catch 语句允许我们定义当 try 代码块发生错误时,所执行的代码块。
finally 语句在 try 和 catch 之后无论有无异常都会执行。
注意: catch 和 finally 语句都是可选的,但你在使用 try 语句时必须至少使用一个。
提示: 当错误发生时, JavaScript 会停止执行,并生成一个错误信息。使用 throw 语句 来创建自定义消息(抛出异常)。如果你将 throw 和 try 、 catch一起使用,就可以控制程序输出的错误信息。
try {
tryCode - 尝试执行代码块
}
catch (err) {
catchCode - 捕获错误的代码块
}
finally {
finallyCode - 无论 try / catch 结果如何都会执行的代码块
}
错误类型
Error 通用错误
EvalError
与eval()有关, 当调用 eval() 失败时抛出。
RangeError
数值变量或参数超出了其有效范围
ReferenceError
一个无效的引用(eg: strict模式下引用了一个未声明的变量等)
SyntaxError
当 JavaScript 语法错误时抛出。
TypeError
变量或参数不属于有效类型