一. 何为promise
本意为承诺的意思;
在编程过程中,会出现异步代码和同步代码,而简单地说,promise的出现就是为了解决异步编程的问题,让异步编程代码变的更加优雅;
异步代码的解决方案:
解决方案有很多种,promise算一种;如下:
- 回调函数
- promise
- generator + co
- async + await(终极解决方案,让写异步代码就像写同步代码那么简单)
二. promise的相关规范
1. promise四大术语
- 解决(fulfill):指一个 promise 成功时进行的一系列操作,如状态的改变、回调的执行。虽然规范中用 fulfill 来表示解决,但在后世的 promise 实现多以 resolve 来指代之。
- 拒绝(reject):指一个 promise 失败时进行的一系列操作。
- 终值(eventual value):所谓终值,指的是 promise 被解决时传递给解决回调的值,由于 promise 有一次性的特征,因此当这个值被传递时,标志着 promise 等待态的结束,故称之终值,有时也直接简称为值(value)。
- 据因(reason):也就是拒绝原因,指在 promise 被拒绝时传递给拒绝回调的值。
2. promise三个状态
- Pending (等待状态):可以迁移至执行态或拒绝态
- Fulfilled (执行状态):不能迁移至其他任何状态,必须拥有一个不可变的终值
- Rejected (拒绝状态):不能迁移至其他任何状态,必须拥有一个不可变的据因
3. promise两个事件
针对三种状态有如下两种转换方向:
- pending --> fulfilled (执行态)
- pending --> rejected (拒绝态)
- 注意: 在状态改变时,在执行器中有两个参数,一个是resolve,一个是reject,他们两个都是函数,在等待到执行态转换过程中调用resolve,在等待到拒绝态转换过程中调用reject函数;
4. promise一个对象
- 就是指promise对象;
三. promise的用法
1. 基本用法
- 只有当代码中有异步代码的时候,才要想起使用promise
(1) 本质
- Pormise本质就是一个构造器
- 在使用的时候需要new一个promise:
new Promise()
(2) 具体代码
var p1 = new Promise();
console.log(p1) //报错 TypeError: Promise resolver undefined is not a function
报错原因:
在创建promise时,需要给promise指定一个执行器,修改如下:
var p1 = new Promise(function (resolve,reject) {
});
console.log(p1)
查看结果:
此结果说明我们已经创建好了promise对象,并且当前对象处于pending状态;
- 继续编写代码如下:
var p1 = new Promise(function (resolve,reject) {
//表示由等待到了执行状态,里面的参数就是指 终值(value)
resolve("买包包");
});
var p1 = new Promise(function (resolve,reject) {
//表示由等待到了拒绝状态,里面的参数就是指 拒因(reason)
reject("没钱")
});
等待状态中只有两种可能转换状态,要么成功,要么失败;
let p1 = new Promise(function (resolve,reject) {
//增加一个判断来确定转换为哪种状态
if(Math.random() > 0.5){
resolve("包包")
}else{
reject("没线")
}
})
(3)then方法
针对上面代码,如何获取终值(包包)或者拒因(没钱)呢?
此时我们就需要利用事件机制
解决方案:
针对3种状态,只有如下两种转换方向:
- pending --> fulfilled
- pendeing --> rejected
在状态转换的过程中,就会触发事件:
- 如果是pending --> fulfiied,就会触发onFulFilled事件
- 如果是pendeing --> rejected,就会触发onRejected事件
因此: 在调用resolve方法或者reject方法的时候,就一定会触发事件。
需要注册onFulFilled事件 和 onRejected事件
针对事件的注册,promise对象提供了then方法,如下:
针对 onFulFilled: 会自动提供一个参数,作为终值(value)
针对 onRejected: 会自动提供一个参数,作为据因(reason)
- 注册事件如下:
let p1 = new Promise(function (resolve,reject) {
//状态转化只能选择一个 如果两种状态都写出来时会默认执行上面的状态,此时就是执行“包包”这个状态
resolve("包包")
reject("没钱")
})
p1.then((value)=>{
console.log(value) //输出 包包
},(err)=>{
console.log("..."+err) //如果转换为拒绝状态,这个地方就会输出 ...没钱
});
2. then的链式调用(相对难)
在每一次执行.then的时候都会返回一个新的promise,而初始状态是pending,返回的新的promise也可以继续.then,这样就相当于可以一直.then下去,这就叫做then的链式调用;
如下:
let p1 = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve("ok")
},2000)
})
let p2 = p1.then(result=>{
console.log(result) // ok
return 123;
},reason=>{
console.log(reason)
})
p2.then((result)=>{
console.log(result) //123
},(reason)=>{
console.log(reason)
})
注意:
- p1.then中不论哪个方法执行,只要不报错,新的p2promise状态都会变成成功态,而方法返回值就是p2promise的终值,如果 p1.then 中没有返回值,那么只要语句不报错,p2promise状态还会变成成功态,此时的终值就是undefined;
- 如果p1.then中的两个方法报错了,p2就转成失败的状态,而p2的终值就是报错的语句
new Promise(resolve => {
setTimeout(()=>{
resolve(10)
},2000)
}).then(result=>{
console.log(`成功:${result}`) // 成功:10
return result * n; // 报错 n is not defiend 这个then返回了一个失败的promise
},reason => {
console.log(`失败:${reason}`)
}).then(result=>{
console.log(`成功:${result}`)
return result * 20;
},reason => {
console.log(`失败:${reason}`); // 失败:ReferenceError: n is not defined
return reason * 10; // NaN 没有报错
}).then(result=>{
console.log(`成功:${result}`); //成功:NaN
return Promise.reject(result * 20)
},reason => {
console.log(`失败:${reason}`)
return Promise.resolve(reason * 10)
}).then(result=>{ //
console.log(`成功:${result}`);
},reason => {
console.log(`失败:${reason}`); // 失败:NaN
})
3. catch、then的顺延机制
如果then中成功或失败的方法没有设置, 会顺延到下一个then中查找;
如下:
new Promise((resolve,reject)=>{
setTimeout(()=>{
reject(10)
},2000)
}).then(result=>{ //此then中没有拒绝状态的事件
console.log(`成功:${result}`)
}).then(result=>{
console.log(`成功:${result}`)
return result*10
},reason => {
console.log(`失败:${reason}`) // 失败:10
return reason*20
}).then(null,reason => {
console.log(`失败:${reason}`)
}).then(result=>{
console.log(`成功:${result}`) // 成功:200
},reason => {
console.log(`失败:${reason}`)
})
在then的链式调用机制中,两个事件参数(result和reason)如果只写执行状态的终值(result)那么后面的reason参数可以不写,也可以写为null,如
then(result=>{console.log(result)})
//或者
then(result=>{console.log(result)},null)
而如果只写拒绝状态的终值(reason),那么前面的reason要写为null,如
then(null,reason => {console.log(reason)})
此时要想不写前面的null,那么就引入了catch
简单的讲,就相当于catch 是then的语法糖
.then(null,reason=>{})
就等价于catch(reason => {})
因此代码就演化为:
new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(10)
},2000)
}).then(result=>{
console.log(`成功:${result}`)
}).catch(reason => {
console.log(`失败:${reason}`)
})
将相当于如果上面状态如果变为执行状态,就执行 .then 语句;如果变为拒绝状态,就执行 .catch 语句;
四. Axios的使用
1. 何为Axios
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。
2. Axios的应用
直接上代码:
- 服务端:
let express = require("express") //引入express模块
let cors = require("cors") //引入cors,解决跨域问题
let app = express(); //生成一个服务器
app.use(cors()) //使用cors解决跨域问题
app.get("/news",(req,res)=>{ //写一个路由
res.json({ //返回一个json数据
code:"1",
msg:{
name:"wangcai",
age:100
}
});
});
app.listen(3000,()=>{ //监听一个端口
console.log("3000 ok")
})
- 客户端:
<body>
<button>获取数据</button>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script> //引入Axios的js包
<script>
document.querySelector("button").onclick = function () { //给button注册点击事件
//向上面创建的api发送请求,返回值如果成功就走.then如果失败就走.catch
axios.get("http://localhost:3000/news").then(res=>{
console.log(res.data) //最终输出结果为 100
}).catch(reason=>{
console.log(reason)
})
}
</script>
</body>