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 方法



worldpress 前端 api_worldpress 前端 api

作者说

此文档只是个人学习笔记,源学习视频请参考:Promise 教程

准备工作

建议学习前端 HTML 知识

配置 Ajax 环境:参考文档

编辑器:Visual Studio Code

Promise 的初体验

如果我们有一个页面(如下),完成的功能是点击按钮后浏览器响应请求并弹出窗口

worldpress 前端 api_ajax_02

需求是点击按钮后 3 s后显示是否中奖!以及中奖率为 30%

  1. window.onload 函数意义在于将 script 标签封装进 head 部分
  2. const 定义变量 btn 用于接收获取按钮对象的返回值
  3. setTimeout 函数内的部分在指定时间后被执行
  4. 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 封装此需求

  1. 首先创建 Promise 对象,使用方法 new Promise,并且方法内有二个函数 resolve 和 reject
  2. 然后将异步任务写入方法内部,我们在判断条件后,中奖的话就调用 resolve 函数,否则调用 reject 函数(形象的说成 resolve 成功,reject 失败)
  3. 而且 resolve 和 reject 函数可以传递参数,传递的参数在实例化 p 对象调用 then 方法的时候又可以被使用
  4. 最后调用 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 模块

需求是读取一个文本,若成功读取则输出文本内容,否则输出错误理由

  1. 在 js 代码中引入模块 fs 使用 require
  2. 读取文件使用方法 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 封装此需求

  1. 首先创建 Promise 对象并且将异步内容封装进去
  2. 然后判断条件,如果读取成功,则调用 resolve 函数,否则调用 reject 函数
  3. 接着调用 then 方法,参数 value 和 reason 分别接收到 resolve 和 reject 函数的参数
  4. 最后在 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 信息

worldpress 前端 api_ajax_03


需求是点击按钮后向免费 API 接口网站发送 url 请求并将返回的数据进行 json 处理后在控制台中显示出来

  1. 免费 API 接口网站:地址
  2. AJAX 发送请求首先需要创建一个 xhr 对象,使用方法 XMLHttpRequest
  3. 然后进行初始化,使用方法 open,其中有二个参数,第一个是请求方法,第二个是请求地址
  4. 接着进行发送,使用方法 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 封装此需求

  1. 首先创建 Promise 对象并且将异步内容封装进去
  2. 异步内容包含按钮元素的获取、按钮事件的绑定及 ajax 请求的实现
  3. 然后判断条件,如果状态码在其中,则调用 resolve 函数,否则调用 reject 函数
  4. 接着调用 then 方法,参数 value 和 reason 分别接收到 resolve 和 reject 函数的参数
  5. 最后在 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 的失败回调

  1. catch 方法的失败回调在创建 Promise 对象后,函数体内调用 reject 函数,那么此对象是失败的
  2. then 方法的成功回调在创建 Promise 对象后,函数体内调用 resolve 函数,那么此对象是成功的
  3. 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(大白话就是只使用成功回调)

  1. 在调用的时候传入的是非 Promise 的对象,那么返回的是成功的 Promise 对象,在调用 then 方法后将进行 value 参数下函数体的执行
  2. 在调用的时候传入的是 Promise 的对象,那么参数的结果决定了返回值的结果,如下列 p3 参数的结果是一个失败,那么 p3 就是一个失败的 Promise 对象,对此对象使用 catch 来执行失败回调,否则控制台会报错!
  3. 另外 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

  1. all 方法内部参数可以为非 Promise 对象和 Promise 对象,如果二个同时传入,且 Promise 对象都是成功的,那么返回的用 all 方法创建的 Promise 对象也是成功的!
  2. 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>