Promise
- 作者说
- 准备工作
- Promise 的初体验
- Promise 实践练习 -- fs 模块
- Promise 实践练习 -- AJAX 请求
- Promise 封装练习 -- fs 模块
- Promise 封装练习 -- AJAX 请求
- Promise 的 API -- then 和 catch 方法
- Promise 的 API -- resolve 方法
- Promise 的 API -- reject 方法
- Promise 的 API -- all 方法
- Promise 的 API -- race 方法
作者说
此文档只是个人学习笔记,源学习视频请参考:Promise 教程
准备工作
建议学习前端 HTML 知识
配置 Ajax 环境:参考文档
编辑器:Visual Studio Code
Promise 的初体验
如果我们有一个页面(如下),完成的功能是点击按钮后浏览器响应请求并弹出窗口
需求是点击按钮后 3 s后显示是否中奖!以及中奖率为 30%
- window.onload 函数意义在于将 script 标签封装进 head 部分
- const 定义变量 btn 用于接收获取按钮对象的返回值
- setTimeout 函数内的部分在指定时间后被执行
- let 定义全局变量,const 定义局部变量,alert 用于浏览器弹窗显示
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Promise 初体验</title>
<script>
window.onload = function(){
// 生成随机数的函数
function rand(m, n){
return Math.ceil(Math.random() * (n-m+1)) + m - 1
}
// 获取元素对象
const btn = document.querySelector('#result')
// 绑定单击事件
btn.addEventListener('click', function(){
// 定时器
setTimeout(() => {
// 获取 1 - 100 的随机数
let n = rand(1, 100)
// 判断
if(n <= 30){
alert('恭喜恭喜!奖品为 100 元 KFC 优惠劵, 中奖号码为 '+ n)
}else{
alert('再接再厉!号码为 ' + n)
}
}, 3000);})}
</script>
</head>
<body>
<h2>Promise 初体验</h2>
<button id="result">点击抽奖</button>
</body>
</html>
接下来我们使用 Promise 封装此需求
- 首先创建 Promise 对象,使用方法 new Promise,并且方法内有二个函数 resolve 和 reject
- 然后将异步任务写入方法内部,我们在判断条件后,中奖的话就调用 resolve 函数,否则调用 reject 函数(形象的说成 resolve 成功,reject 失败)
- 而且 resolve 和 reject 函数可以传递参数,传递的参数在实例化 p 对象调用 then 方法的时候又可以被使用
- 最后调用 then 方法,此方法内又可以认为有二个函数,且各自传递一个参数,分别是 value 和 reason ,我们把之可以看成形式参数,那么实际参数分别是 resolve 和 reject 中传入的参数,也就是说在 Promise 对象实例化过程中定义的变量可以通过函数在 then 方法的函数体中得到使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Promise 初体验</title>
<script>
window.onload = function(){
// 生成随机数的函数
function rand(m, n){
return Math.ceil(Math.random() * (n-m+1)) + m - 1
}
const btn = document.querySelector('#result')
btn.addEventListener('click', function(){
// resolve 解决 函数类型的数据
// reject 拒绝 函数类型的数据
const p = new Promise((resolve, reject) => {
setTimeout(() => {
let n = rand(1, 100)
if(n <= 30){
resolve(n) // 将 promise 对象的状态设置为 成功
}else{
reject(n) // 将 promise 对象的状态设置为 失败
}
}, 3000);
})
// 调用 then 函数
p.then((value)=>{
alert('恭喜恭喜!奖品为 100 元 KFC 优惠劵, 中奖号码为 ' + value)
}, (reason)=>{
alert('再接再厉!号码为 ' + reason)
})
console.log(p)
})
}
</script>
</head>
<body>
<h2>Promise 初体验</h2>
<button id="result">点击抽奖</button>
</body>
</html>
Promise 实践练习 – fs 模块
需求是读取一个文本,若成功读取则输出文本内容,否则输出错误理由
- 在 js 代码中引入模块 fs 使用 require
- 读取文件使用方法 readFile,且其中传入二个参数,第一个参数为文本的路径,第二个参数为一个函数体,函数体内传入二个参数(err 和 data),err 代表错误内容,data 代表二进制文本内容
// 创建文件对象
const fs = require('fs')
fs.readFile('./resource/content.txt', (err, data) => {
// 如果出错 则抛出错误
if(err) throw err
// 否则输出文本内容
console.log(data.toString())
})
接下来我们使用 Promise 封装此需求
- 首先创建 Promise 对象并且将异步内容封装进去
- 然后判断条件,如果读取成功,则调用 resolve 函数,否则调用 reject 函数
- 接着调用 then 方法,参数 value 和 reason 分别接收到 resolve 和 reject 函数的参数
- 最后在 then 方法的函数体中完成需求【读取文本内容】和【输出错误】
// 创建文件对象
const fs = require('fs')
let p = new Promise((resolve, reject) => {
fs.readFile('./resource/content.txt', (err, data) => {
// 如果失败
if(err) reject(err)
// 如果成功
resolve(data)
})})
// 调用 then 函数
p.then(value => {
console.log(value.toString())
}, reason => {
console.log(reason)
})
Promise 实践练习 – AJAX 请求
如果我们有下面一个页面,完成的功能是点击按钮后在控制台中返回 json 信息
需求是点击按钮后向免费 API 接口网站发送 url 请求并将返回的数据进行 json 处理后在控制台中显示出来
- 免费 API 接口网站:地址
- AJAX 发送请求首先需要创建一个 xhr 对象,使用方法 XMLHttpRequest
- 然后进行初始化,使用方法 open,其中有二个参数,第一个是请求方法,第二个是请求地址
- 接着进行发送,使用方法 send,最后处理响应结果(大白话就是看 xhr 对象的状态码是否变为 4,请求的响应码是不是 2xx)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Promise 封装 AJAX</title>
<script>
window.onload = function(){
// 接口地址 https://api.apiopen.top/getJoke
// 获取元素对象
const btn = document.querySelector('#result')
// 绑定事件
btn.addEventListener('click', function(){
// 1. 创建对象
const xhr = new XMLHttpRequest()
// 2. 初始化
xhr.open('GET', 'https://api.apiopen.top/getJoke')
// 3. 发送
xhr.send()
// 4. 处理响应结果
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
// 判断响应状态码
if(xhr.status >= 200 && xhr.status < 300){
// 控制台输出响应体
console.log(xhr.response)
}else{
// 控制台输出响应状态码
console.log(xhr.status)
}
}
}
})
}
</script>
</head>
<body>
<h2>封装 AJAX 操作</h2>
<button id="result">点击发送</button>
</body>
</html>
接着我们使用 Promsie 封装此需求
- 首先创建 Promise 对象并且将异步内容封装进去
- 异步内容包含按钮元素的获取、按钮事件的绑定及 ajax 请求的实现
- 然后判断条件,如果状态码在其中,则调用 resolve 函数,否则调用 reject 函数
- 接着调用 then 方法,参数 value 和 reason 分别接收到 resolve 和 reject 函数的参数
- 最后在 then 方法的函数体中完成需求【读取响应体】和【读取响应状态码】
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Promise 封装 AJAX</title>
<script>
window.onload = function(){
const p = new Promise((resolve, reject) => {
const btn = document.querySelector('#result')
btn.addEventListener('click', function(){
const xhr = new XMLHttpRequest()
xhr.open('GET', 'http://127.0.0.1:3000/promise-ajax')
xhr.send()
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
if(xhr.status >= 200 && xhr.status < 300){
resolve(xhr.response)
}else{
reject(xhr.status)
}
}
}
})
}
)
// 调用 then 函数
p.then(value => {
console.log(value)
}, reason => {
console.log(reason)
})
}
</script>
</head>
<body>
<h2>封装 AJAX 操作</h2>
<button id="result">点击发送</button>
</body>
</html>
Promise 封装练习 – fs 模块
在之前读取文本内容的基础上实现一个函数体的封装,也就是调用函数,传入参数(文件路径)
需求是封装一个函数 mineReadFile 读取文件内容,参数为 path 文件路径,返回一个 Promise 对象
- mineReadFile 函数体返回值是一个 Promise 对象,也就是在调用函数的时候就可以调用 then 方法
function mineReadFile(path){
return new Promise((resolve, reject) => {
// 读取文件
require('fs').readFile(path, (err, data) => {
// 判断
if(err) reject(err)
// 成功
resolve(data)
})
})
}
mineReadFile('./resource/content.txt').then(value => {
console.log(value.toString())
}, reason => {
console.log(reason)
})
Promise 封装练习 – AJAX 请求
在之前发送 ajax 请求的基础上实现一个函数体的封装,也就是调用函数,传入参数(url)
需求是封装一个函数 sendAJAX 请求指定网址,参数为 url 网址,返回一个 Promise 对象
- sendAJAX 函数体返回值是一个 Promise 对象,也就是在调用函数的时候就可以调用 then 方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Promise 封装练习</title>
</head>
<body>
<h1>Exercise</h1>
<button id="result">获取数据</button>
<script>
function sendAJAX(url){
return new Promise((resolve, reject) => {
const 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('#result')
btn.addEventListener('click', function(){
sendAJAX('https://api.apiopen.top/getJoke').then(value => {
console.log(value)
}, reason => {
console.log(reason)
})
})
</script>
</body>
</html>
Promise 的 API – then 和 catch 方法
需求是验证 then 方法的成功和失败回调及 catch 的失败回调
- catch 方法的失败回调在创建 Promise 对象后,函数体内调用 reject 函数,那么此对象是失败的
- then 方法的成功回调在创建 Promise 对象后,函数体内调用 resolve 函数,那么此对象是成功的
- then 方法的失败回调在创建 Promise 对象后,函数体内调用 reject 函数,那么此对象是失败的
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Promise API</title>
</head>
<body>
<script>
// 1. 验证 Promise 内的函数体是在创建的同时被同步调用的
// let p = new Promise((resolve, reject) => {
// console.log('First')
// })
// console.log('Second')
// 2. catch 方法 -- 用于失败回调
// let p = new Promise((resolve, reject) => {
// reject('Error')
// })
// p.catch(reason => {
// console.log(reason)
// })
// 3. then 方法 -- 用于成功和失败回调
// 用于生成随机数
function rand(m, n){
return Math.ceil(Math.random() * (n-m+1)) + m - 1
}
let p = new Promise((resolve, reject) => {
a = rand(1, 10)
if(a > 5){
reject('Error')
}else{
resolve('OK')
}
})
p.then(value => {
console.log(value)
}, reason => {
console.log(reason)
})
</script>
</body>
</html>
Promise 的 API – resolve 方法
Promise 实例化的过程从 new Promise 修改为 Promise.resolve(大白话就是只使用成功回调)
- 在调用的时候传入的是非 Promise 的对象,那么返回的是成功的 Promise 对象,在调用 then 方法后将进行 value 参数下函数体的执行
- 在调用的时候传入的是 Promise 的对象,那么参数的结果决定了返回值的结果,如下列 p3 参数的结果是一个失败,那么 p3 就是一个失败的 Promise 对象,对此对象使用 catch 来执行失败回调,否则控制台会报错!
- 另外 Promise 对象有二个属性,PromiseState 和 PromiseResult,分别表示成功和失败的状态及状态值
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Promise API-resolve</title>
</head>
<body>
<script>
// 如果传入的参数为非 Promise 类型的对象,则返回的结果为成功的 Promise 对象
let p1 = Promise.resolve(521)
// 如果传入的参数为 Promise 类型的对象,则参数的结果决定了 resolve 的结果
let p2 = Promise.resolve(new Promise((resolve, reject) => {
resolve('OK')
// reject('Error')
}))
let p3 = Promise.resolve(new Promise((resolve, reject) => {
// resolve('OK')
reject('Error')
}))
console.log(p1) // [[PromiseState]]: "fulfilled" [[PromiseResult]]: 521
console.log(p2) // [[PromiseState]]: "fulfilled" [[PromiseResult]]: "OK"
// 此处控制台会报错,因为出现了错误的 Promise 对象缺没有执行回调函数,我们在后面补充一条回调语句
console.log(p3) // [[PromiseState]]: "rejected" [[PromiseResult]]: "Error"
// 针对 p3 的回调语句
// p3.catch(reason => {
// console.log(reason)
// })
// 针对 p1 的成功回调语句
p1.then(value => {
console.log(value)
})
// 针对 p2 的成功回调语句
p2.then(value => {
console.log(value)
})
</script>
</body>
</html>
Promise 的 API – reject 方法
Promise 实例化的过程从 new Promise 修改为 Promise.reject(大白话就是只使用失败回调)
- 此方法创建的全是失败的 Promise 对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Promise API-reject</title>
</head>
<body>
<script>
let p1 = Promise.reject(521)
let p2 = Promise.reject('i love you.')
let p3 = Promise.reject(new Promise((resolve, reject) => {
resolve('OK')
}))
// 此处出现了 3 个错误的 Promise 对象,控制台会报错,我们在后面定义对应的回调函数
console.log(p1) // [[PromiseState]]: "rejected" [[PromiseResult]]: 521
console.log(p2) // [[PromiseState]]: "rejected" [[PromiseResult]]: "i love you."
console.log(p3) // [[PromiseState]]: "rejected" [[PromiseResult]]: Promise
p1.catch(reason => {
console.log(reason)
})
p2.catch(reason => {
console.log(reason)
})
p3.catch(reason => {
console.log(reason)
})
</script>
</body>
</html>
Promise 的 API – all 方法
Promise 实例化的过程从 new Promise 修改为 Promise.all
- all 方法内部参数可以为非 Promise 对象和 Promise 对象,如果二个同时传入,且 Promise 对象都是成功的,那么返回的用 all 方法创建的 Promise 对象也是成功的!
- all 方法对应 Promise 对象只要存在失败的情况,返回的用 all 方法创建的 Promise 对象也是失败的!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Promise API-all</title>
</head>
<body>
<script>
// 1. all 方法对于 Promise 实例都是正确的情况
// let p1 = new Promise((resolve, reject) => {
// resolve('OK')
// })
// let p2 = Promise.resolve('Success')
// let p3 = Promise.resolve('OK')
// const result = Promise.all([p1, p2, p3,'abc']) // [[PromiseState]]: "fulfilled" [[PromiseResult]]: Array(4)
// console.log(result)
// 2. all 方法对于 Promise 实例存在错误的情况
let p1 = new Promise((resolve, reject) => {
reject('Error')
})
let p2 = Promise.resolve('Success')
let p3 = Promise.resolve('OK')
const result = Promise.all([p1, p2, p3]) // [[PromiseState]]: "rejected" [[PromiseResult]]: "Error"
console.log(result)
</script>
</body>
</html>
Promise 的 API – race 方法
Promise 实例化的过程从 new Promise 修改为 Promise.race(赛跑方法)
- race 方法内部参数可以为非 Promise 对象和 Promise 对象,如果二个同时传入,则观察哪一个对象是被先创建的,race 返回的结果是先创建的 Promise 对象,此外 race 方法里参数的传入的先后顺序也很重要!(*)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Promise API-race</title>
</head>
<body>
<script>
// 1. 第一个 Promise 对象先执行
// let p1 = new Promise((resolve, reject) => {
// resolve('Yeah')
// })
// let p2 = Promise.resolve('Success')
// let p3 = Promise.resolve('OK')
// const result = Promise.race([p1, p2, p3]) // [[PromiseState]]: "fulfilled" [[PromiseResult]]: "Yeah"
// console.log(result)
// 2. 第二个 Promise 对象先执行
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Yeah')
}, 2000);
})
let p2 = Promise.resolve('Success')
let p3 = Promise.resolve('OK')
const result = Promise.race([p1, p2, p3])
console.log(result) // [[PromiseState]]: "fulfilled" [[PromiseResult]]: "Success"
</script>
</body>
</html>