一、什么是 Promise?
Promise
是 ES6 引入的进行异步编程的新的解决方案。备注:旧的是单纯的回调函数
从语法上来说:它就是一个构造函数,能够封装异步的工作,并且对后果进行解决。从性能上来说:Promise
对象用来封装一个异步操作并能够获取其 胜利 / 失败
的后果值。
Promise
最大的益处在于能够解决回调天堂的问题,并且它在指定回调与错误处理这块要更加的灵便与不便,而且 Promise
在古代我的项目当中,无论是 Web 还是 App 的我的项目当中都利用的非常宽泛,无论是前端还是后端都能够看到它的身影,同时它也是当初 面试的高频题目。
二、Promise 初体验
2.1 在 nodejs 环境下读取文件内容
需要:读取当前目录下的 file 文件夹下的 content.txt 的内容并输入。
// 引入 fs 模块
const fs = require(‘fs’)
// 回调函数模式
// fs.readFile(‘./file/content.txt’, (err, data) => {
// // 如果谬误, 则抛出谬误
// if(err) throw err
// console.log(data.toString());
// })
// Promise 旧式
let p = new Promise((resolve, reject) => {
fs.readFile(‘./file/content.txt’, (err, data) => {
if (err) reject(err);
resolve(data);
})
})
p.then(value =>{
console.log(value.toString());
},reason =>{
console.log(reason);
})
output:
Hello Promise,This is Content
1
2.2 封装 AJAX
需要:应用 Promise 封装 AJAX 并读取接口数据并输入 接口地址:https://api.apiopen.top/getJoke
<!DOCTYPE html>
<html lang=”en”>
<head>
<meta charset=”UTF-8″>
<meta name=”viewport” content=”width=device-width, initial-scale=1.0″>
<title>Document</title>
<!– 引入 bootstrap 的款式 –>
<link rel=”stylesheet” href=”https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.css”>
</head>
<body>
<div class=”container”>
<h2 class=”page-header”>Promise 封装 AJAX 操作 </h2>
<button class=”btn btn-primary” id=”btn”> 点击发送 AJAX</button>
</div>
<script>
const btn = document.querySelector(‘#btn’)
btn.onclick = function () {
const p = new Promise((resolve, reject) => {
xhr = new XMLHttpRequest()
xhr.open(‘GET’, ‘https://api.apiopen.top/getJoke’)
xhr.send()
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.response)
} else {
reject(xhr.status)
}
}
}
})
p.then(value => {
console.log(value);
}, reason => {
console.warn(reason);
})
}
</script>
</body>
</html>
三、怎么应用 Promise?
在理解怎么应用 Promise 之前,咱们先来看看 Promise 实例对象的两个属性:「PromiseState」和「PromiseResult」
3.1 Promise 的状态
每一个实例对象都领有一个属性叫做 「PromiseState」,这个属性有三个值,别离是:
- pending 「未决定的」
- resolved / fullfilled 「胜利」
- rejected 「失败」
状态的扭转
- pending 变为 resolved
- pending 变为 rejected
阐明:只有这 2 种,且一个 promise 对象只能扭转一次,扭转后不可批改 无论变为胜利还是失败,都会有一个后果数据 胜利的后果数据个别称为 value,失败的后果数据个别称为 reason
3.2 Promise 的后果
每一个实例对象都领有一个属性叫做 「PromiseResult」,它用于保留实例对象(异步工作) 「胜利或失败」 的后果。
只有 resolve和reject 能够批改该属性的值。
四、Promise 的 API
4.1 resolve 办法
如果传入的参数为非 Promise 类型的对象,则返回的后果为胜利 promise 对象 如果传入的参数为 Promise 对象,则参数的后果决定了 resolve 的后果
const p1 = Promise.resolve(520)
console.log(p1); // PromiseState => fulfilled
const p2 = Promise.resolve(new Promise((resolve, reject) => {
reject(‘111’)
}))
// 此处外部有一个失败的回调, 然而没有解决. 所以会产生报错
console.log(p2); // PromiseState => rejected 报错:Uncaught (in promise) 111
p2.catch(reason => {
console.log(reason); // 111
})
4.2 reject 办法
永远返回一个 rejected「失败」的 Promise 对象。
// const p = Promise.reject(521)
// 留神此处会产生一个报错, 起因是外部有一个失败的 Promise 然而没有对应的回调来解决它
// console.log(p); // PromiseState rejected
const p2 = Promise.reject(new Promise((resolve, reject) => {
resolve(‘123’)
}))
console.log(p2); // PromiseState rejected
p2.then(value =>{
console.log(value);
},reason =>{
console.log(reason); // 失败的后果 是一个 Promise 对象, 这个 Promise 对象的状态为胜利
})
4.3 all 办法
语法:
Promise.all(Array) // Array 一组 Promise 对象
1
阐明:返回一个新的 Promise,只有所有的 Promise 都胜利才胜利,只有有一个失败了就 间接失败
第一种状况,全部都是 resolved:
const p1 = new Promise((resolve, reject) => {
resolve(‘ok’)
})
const p2 = Promise.resolve(‘success’)
const p3 = Promise.resolve(‘Oh Yeah’)
const result = Promise.all([p1, p2, p3])
console.log(‘result: ‘, result);
// PromiseState “fulfilled”
// PromiseResult [‘ok’,’success’,’Oh Yeah’]
第二种状况,有一个是 rejected:
const p1 = new Promise((resolve, reject) => {
resolve(‘ok’)
})
const p2 = Promise.reject(‘error info’)
const p3 = Promise.resolve(‘Oh Yeah’)
const result = Promise.all([p1, p2, p3])
console.log(‘result: ‘, result);
// PromiseState “rejected”
// PromiseResult “error info”
4.4 race 办法
race 函数返回一个 Promise,它能够是实现(resolved),也能够是失败(rejected),这要取决于第一个实现的是哪个。
第一种状况,先实现的是 「P1 => resolved」 :
const p1 = new Promise((resolve, reject) => {
resolve(‘ok’)
})
const p2 = Promise.reject(‘error’)
const p3 = Promise.resolve(‘Oh Yeah’)
const result = Promise.race([p1, p2, p3])
console.log(result); // PromiseState:”fulfilled” PromiseResult:”ok”
第二种状况,先实现的是 「P2 => rejected」:
// 第一钟测试形式:
// 留神:这里扭转的是 race 办法参数的地位
const p1 = new Promise((resolve, reject) => {
resolve(‘ok’)
})
const p2 = Promise.reject(‘error info’)
const p3 = Promise.resolve(‘Oh Yeah’)
const result = Promise.race([p2, p1, p3])
console.log(result); // PromiseState:”rejected” PromiseResult:”error info”
// 第二钟测试形式:
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(‘ok’)
}, 1000);
})
const p2 = Promise.reject(‘error info’)
const p3 = Promise.resolve(‘Oh Yeah’)
const result = Promise.race([p1, p2, p3])
console.log(result); // PromiseState:”rejected” PromiseResult:”error info”
4.5 实例对象的 then 办法
then 办法领有返回值,返回值是一个 Promise 对象。状态是由回调函数的执行后果来决定.
- 返回后果是非 Primose 类型, 状态为胜利,返回值为 then 办法中的返回值(下方例子中的 123 即为返回值),办法中如果没有 return 则为 undefined
- 如果返回值是一个 Promise 对象,那么外部 Promise 对象返回的状态和值决定内部 then 办法的状态和值
- 如果抛出谬误,则 then 办法状态为 rejected, 值为抛出的值
提醒:then 办法的返回值与 async 润饰的函数的返回值截然不同
const p = new Promise((resolve, reject) => {
resolve(‘ok’)
})
const result = p.then(value => {
// 1. 返回一般数据 状态为胜利, 没有 return 状态也为胜利, 值为 undefined
// return 123;
// 2. 抛出谬误, 状态为失败, 值为抛出的值
// throw ‘ 出错啦 ’
// 3. 返回一个 Promise , 外部的 Promise 决定内部 then 办法的状态及返回值
return new Promise((resolve, reject) => {
// resolve()
// reject()
throw ‘ 出错啦!’;
})
}, err => {
console.error(err);
})
console.log(result); // PromiseState: “rejected” PromiseResult: “ 出错啦!”
非凡状况:
then 办法冀望的参数是一个函数,如果不是函数则会产生 Promise 穿透(值穿透),状态为上一个 Promise 的状态,值为上一个 Promise 的值。
// 非凡状况:
const p = new Promise((resolve, reject) => {
resolve(‘ok’)
// reject(‘error’)
})
const result = p.then(console.log(123))
console.log(result); // PromiseState: “fulfilled” PromiseResult: “ok”
1234567
略微加点难度:
Promise.resolve(‘foo’)
.then(Promise.resolve(‘bar’))
.then(function(result){
console.log(result)
})
当然,输入的后果为 foo。问其起因,答案如题——Promise 值穿透 解释:.then 或者 .catch 的参数冀望是函数,传入非函数则会产生值穿透。
再来一道:
Promise.resolve(1)
.then(function(){return 2})
.then(Promise.resolve(3))
.then(console.log)
output: 2 解释:Promise.resolve(3) 不是函数,产生了值穿透
持续来:
Promise.resolve(1)
.then(function(){return 2})
.then(function(){return Promise.resolve(3)})
.then(console.log)
output:3
五、async 和 await
5.1 async
应用 async 关键字 润饰的函数 返回值永远为 Promise 对象,这个 Prmose 对象的状态与 then 办法返回值的状态都是利用的同一种规定。
一个最简略的例子:
async function main(){
}
let result = main()
console.log(result); // “fulfilled” “undefined”
5.2 await
await 右侧的表达式个别为 promise 对象,但也能够是其它的
- 右侧没有 Promise 的状况: 间接将此值作为 await 的返回值
- 右侧为胜利的 Promise: 返回的是 promise 胜利的值
- 右侧为失败的 Promise: 抛出异样, 须要应用 try catch 捕捉解决
留神:await 必须写在 async 函数中,但 async 函数中能够没有 await
async function main() {
// 1. 右侧没有 Promise 的状况: 间接将此值作为 await 的返回值
let result = await ‘str’
console.log(result); // result => str info => fulfilled str
return result
// 2. 右侧为胜利的 Promise: 返回的是 promise 胜利的值
// let p1 = new Promise((resolve, reject) => {
// resolve(‘ok’)
// })
// let result1 = await p1
// console.log(result1); // result => ok info => fulfilled ok
// return result1
// 3. 右侧为失败的 Promise: 抛出异样, 须要应用 try catch 捕捉解决
// let p2 = new Promise((resolve, reject) => {
// reject(‘error’)
// })
// try {
// let result2 = await p2
// } catch (e) {
// console.log(e); // e => error info => fulfilled error
// return e
// }
}
const info = main()
console.log(info);
六、async 与 await 联合发送 AJAX
<!DOCTYPE html>
<html lang=”en”>
<head>
<meta charset=”UTF-8″>
<meta name=”viewport” content=”width=device-width, initial-scale=1.0″>
<title>Document</title>
<!– 引入 bootstrap 的款式 –>
<link rel=”stylesheet” href=”https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.css”>
</head>
<body>
<div class=”container”>
<h2 class=”page-header”>async 与 await 封装 AJAX 操作 </h2>
<button class=”btn btn-primary” id=”btn”> 点击发送 AJAX</button>
</div>
<script>
function sendAJAX(url) {
return new Promise((resolve, reject) => {
xhr = new XMLHttpRequest()
xhr.responseType = ‘json’
xhr.open(‘GET’, url)
xhr.send()
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.response)
} else {
reject(xhr.status)
}
}
}
})
}
const btn = document.querySelector(‘#btn’)
btn.addEventListener(‘click’, async function () {
let result = await sendAJAX(‘https://api.apiopen.top/getJoke’)
console.log(result);
})
</script>
</body>
</html>