第四阶段-promise(未学完)

  一:目录

  1. promise介绍与基本使用
  2. promise API
  3. promise关键问题
  4. promise自定义封装
  5. async与await

二:第 1 章:Promise 的理解和使用

1、概念:

​ Promise是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。

通俗讲,Promise是一个许诺、承诺,是对未来事情的承诺,承诺不一定能完成,但是无论是否能完成都会有一个结果。

  • Pending 正在做。。。
  • Resolved 完成这个承诺
  • Rejected 这个承诺没有完成,失败了

​ Promise 用来预定一个不一定能完成的任务,要么成功,要么失败

​ 在具体的程序中具体的体现,通常用来封装一个异步任务,提供承诺结果

Promise 是异步编程的一种解决方案,主要用来解决回调地狱的问题,可以有效的减少回调嵌套。真正解决需要配合async/await

2、特点:

​ (1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:Pending(进行中)、Resolved(已完成,又称Fulfilled)和Rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。

​ (2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从Pending变为Resolved和从Pending变为Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。

3、缺点:

​ (1)无法取消Promise,一旦新建它就会立即执行,无法中途取消。和一般的对象不一样,无需调用。

​ (2)如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。

​ (3)当处于Pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)

三:Promise的理解和使用

3.1: Promise是什么?

1:理解

抽象表达:

1) Promise 是一门新的技术(ES6 规范)

2)Promise 是 JS 中进行异步编程的新解决方案

异步编程:

   1: fs 文件操作 (fs是node.js下面的一个模块,可以对计算机的磁盘进行读写操作)

       

   2: 数据库操作

   3:ajax

   4:定时器

## 异步编程

<!-- fs 文件操作 -->

require('fs').readFile('./index.html',(err,data)=>{})

<!-- 数据库操作 -->

<!-- ajax -->

$.get('/server',(data)=>{})

<!-- 定时器 -->

setTimeout(()=>{},2000);

备注:旧方案是单纯使用回调函数

具体表达:
  1. 从语法上来说: Promise 是一个构造函数

  2. 从功能上来说: promise 对象用来封装一个异步操作并可以获取其成功/ 失败的结果值

//promise初体验.html


<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link crossorigin='anonymous' href="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap.min.css"
        rel="stylesheet">
</head>

<body>
    <div class="container">
        <h2 class="page-header">Promise 初体验</h2>
        <button class="btn btn-primary" id="btn">点击抽奖</button>
    </div>

    <script>
        //生成随机数
        function rand(m, n) {
            return Math.ceil(Math.random() * (n - m + 1)) + m - 1;
        }

        /**
           点击按钮,  1s 后显示是否中奖(30%概率中奖)
               若中奖弹出    恭喜恭喜, 奖品为 10万 RMB 劳斯莱斯优惠券
               若未中奖弹出  再接再厉
       */

        //实现方法一:
        // 1:获取元素
        const btn = document.querySelector('#btn');
        // 2:绑定点击事件
        btn.addEventListener('click', function () {
            setTimeout(() => {
                let n = rand(1, 100);
                if (n <= 30) {
                    alert('恭喜恭喜, 奖品为 10万 RMB 劳斯莱斯优惠券')
                } else {
                    alert('再接再厉')
                }
            }, 10000);


            //实现方法二:Promise 形式实现
            // resolve 解决  函数类型的数据
            // reject  拒绝  函数类型的数据
            const p = new Promise((resolve, reject) => {
                setTimeout(() => {
                    let n = rand(1, 100);
                    if (n <= 30) {
                        //promise的作用:除了封装异步操作之外,还可以获取异常任务当中成功和失败的结果值;可以把n当作结果值,传给resolve,reject
                        resolve(n); // 将 promise 对象的状态设置为 『成功』
                    } else {
                        reject(n);  // 将 promise 对象的状态设置为 『失败』
                    }
                }, 1000)
            })

            console.log(p);

            //调用 then 方法:通过then方法指定成功和失败时的回调
            // value 值
            // reason 理由
            //()=>{} 参数一是成功的回调
            // ()=>{} 参数二是失败饿回调
            p.then((value) => {
                alert('恭喜恭喜, 奖品为 10万 RMB 劳斯莱斯优惠券, 您的中奖数字为 ' + value);
            }, (reason) => {
                alert('再接再厉, 您的号码为 ' + reason);
            })
        })
    </script>
</body>

</html>

promise实践练习

//js



// 需求;读取resource下的conten.txt内容
const { rejects } = require('assert');
const fs = require('fs');

// 方法一:回调函数 形式
fs.readFile('./resource/content.txt', (err, data) => {
    //如果有错,则抛出错误
    if (err) throw err;
    // 输出文件内容
    console.log(data.toString());
});

// 方法二:promise 形式
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);
})
//ajax操作的封装.html
//需求:点击按钮,给接口地址发送ajax请求,获取信息

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Promise 封装 AJAX</title>
    <link crossorigin='anonymous' href="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div class="container">
        <h2 class="page-header">Promise 封装 AJAX 操作</h2>
        <button class="btn btn-primary" id="btn">点击发送 AJAX</button>
    </div>
    <script>
        //接口地址 https://api.apiopen.top/getJoke
        //获取元素对象
        const btn = document.querySelector('#btn');

        btn.addEventListener('click', function(){
            //创建 Promise
            const p = new Promise((resolve, reject) => {
                //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){
                        //判断响应状态码 2xx   
                        if(xhr.status >= 200 && xhr.status < 300){
                            //控制台输出响应体
                            resolve(xhr.response);
                        }else{
                            //控制台输出响应状态码
                            reject(xhr.status);
                        }
                    }
                }
            });
            //调用then方法
            p.then(value=>{
                console.log(value);
            }, reason=>{
                console.warn(reason);
            });
        });
    </script>
</body>
</html>

promise封装练习-fs模块.js

//js


/**
 * 封装一个函数 mineReadFile 读取文件内容
 * 参数:  path  文件路径
 * 返回:  promise 对象
 */
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);
});

node.js的内置方法---util.promisify方法

对于使用promise,我们不需要每个方法都去手动的进行封装,可以借助util.promisify()方法,将原来的回调函数转变成promise的·函数,这样代码使用起来更加方便!

//js

/**
 * 封装一个函数 mineReadFile 读取文件内容
 * 参数:  path  文件路径
 * 返回:  promise 对象
 */
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操作.js


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Promise封装AJAX操作</title>
</head>
<body>
    <script>
        /**
         * 封装一个函数 sendAJAX 发送 GET AJAX 请求
         * 参数   URL
         * 返回结果 Promise 对象
         */
        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);
                        }
                    }
                }
            });
        }
    
        sendAJAX('https://api.apiopen.top/getJok')
        .then(value => {
            console.log(value);
        }, reason => {
            console.warn(reason);
        });
    </script>
</body>
</html>

2:  promise的状态改变

3、Promise中的常用 API 概述

此处列举几个最常用的API的概述,如果想看详细描述的可以继续往下看下方的 Promise方法的具体使用 描述

Ⅰ- Promise 构造函数: Promise (excutor) {}

(1) executor 函数: 执行器 (resolve, reject) => {}

(2) resolve 函数: 内部定义成功时我们调用的函数 value => {}

(3) reject 函数: 内部定义失败时我们调用的函数 reason => {}

说明: executor 会在 Promise 内部立即同步调用,异步操作在执行器中执行,换话说Promise支持同步也支持异步操作

Ⅱ-Promise.prototype.then 方法: (onResolved, onRejected) => {}

(1) onResolved 函数: 成功的回调函数 (value) => {}

(2) onRejected 函数: 失败的回调函数 (reason) => {}

说明: 指定用于得到成功 value 的成功回调和用于得到失败 reason 的失败回调 返回一个新的 promise 对象

Ⅲ-Promise.prototype.catch 方法: (onRejected) => {}

(1) onRejected 函数: 失败的回调函数 (reason) => {}

说明: then()的语法糖, 相当于: then(undefined, onRejected)

(2) 异常穿透使用:当运行到最后,没被处理的所有异常错误都会进入这个方法的回调函数中

//html


<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        let p1 = Promise.resolve(521);
        // 如果传入的参数为 非promise类型的对象,则返回的结果为成功promise对象
        // 如果传入的参数为promse 对象,则参数的结果决定了 resolve 的结果
        let p2 = Promise.resolve(new Promise((resolve, reject) => {
            // resolve('ok');
            reject('error');
        }));
        console.log(p2);

        // catch()   处理错误的回调函数
        p2.catch(reason => {
            console.log(reason);
        })
    </script>
</body>

</html>
Ⅳ-Promise.resolve 方法: (value) => {}

(1) value: 成功的数据或 promise 对象

说明: 返回一个成功/失败的 promise 对象,直接改变promise状态

  let p3 = Promise.reject(new Promise((resolve, reject) => {  resolve('OK'); }));      
  console.log(p3);

Ⅴ-Promise.reject 方法: (reason) => {}

(1) reason: 失败的原因

说明: 返回一个失败的 promise 对象,直接改变promise状态,代码示例同上

//html


<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        // let p = Promise.reject(521);
        // let p2 = promise.reject('iloveyou');
        // 传入什么参数就返回什么参数
        let p3 = promise.reject(new promise((resolve, reject) => {
            resolve('ok');
        }));
        console.log(p3);
    </script>
</body>

</html>
Ⅵ-Promise.all 方法: (promises) => {}

promises: 包含 n 个 promise 的数组

说明: 返回一个新的 promise, 只有所有的 promise 都成功才成功, 只要有一 个失败了就直接失败

//html


<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        let p1 = new Promise((resolve, reject) => {
            resolve('ok');
        });

        // let p2 = Promise.resolve('success');
        let p2 = Promise.reject('error');
        let p3 = Promise.resolve('oh yeah');

        const result = Promise.all([p1, p2, p3]);
        console.log(result);
    </script>
</body>

</html>
Ⅶ-Promise.race 方法: (promises) => {}

(1) promises: 包含 n 个 promise 的数组

说明: 返回一个新的 promise, 第一个完成的 promise 的结果状态就是最终的结果状态,

如p1延时,开启了异步,内部正常是同步进行,所以p2>p3>p1,结果是P2

//html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        let p1 = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('ok');
            }, 1000)
        })

        let p2 = Promise.resolve('success');
        let p3 = Promise.resolve('oh yeah');

        // 调用
        const result = Promise.race([p1, p2, p3]);
        console.log(result);
    </script>
</body>

</html>

4、Promise的几个关键问题

Ⅰ-如何改变 promise 的状态?

(1) resolve(value): 如果当前是 pending 就会变为 resolved

(2) reject(reason): 如果当前是 pending 就会变为 rejected

(3) 抛出异常: 如果当前是 pending 就会变为 rejected

//html


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        let p = new Promise((resolve,reject)=>{
            // 1:resolve 函数
            resolve('ok');  //pending => fulfilled(resolved)
            // 2: reject 函数
            reject('error'); //pending => rejected
            //  3: 抛出错误
            throw '出问题了';
        })
    </script>
</body>
</html>
Ⅱ-一个 promise 指定多个成功/失败回调函数, 都会调用吗?
这里指定多个回调函数,就是then()方法;
如果我们使用then方法,为一个promise对象去指定多个回调,这些回调是不是都会执行?当promise改变对应状态时都会调用。
//html


<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        let p = new Promise((resolve, reject) => {
            resolve('ok');
        });

        // 指定回调 1
        p.then(value => {
            console.log(value);
        });

        // 指定回调 2
        p.then(value => {
            alert(value);
        })
    </script>
</body>

</html>

 

Ⅲ- 改变 promise 状态和指定回调函数谁先谁后?
是指执行的谁先谁后! 

(1) 都有可能, 正常情况下是先指定回调再改变状态, 但也可以先改状态再指定回调

​ 先指定回调再改变状态(异步):先指定回调--> 再改变状态 -->改变状态后才进入异步队列执行回调函数

​ 先改状态再指定回调(同步):改变状态 -->指定回调 并马上执行回调

(2) 如何先改状态再指定回调? -->注意:指定并不是执行

​ ① 在执行器中直接调用 resolve()/reject() -->即,不使用定时器等方法,执行器内直接同步操作

​ ② 延迟更长时间才调用 then() -->即,在.then()这个方法外再包一层例如延时器这种方法

(3) 什么时候才能得到数据?

​ ① 如果先指定的回调, 那当状态发生改变时, 回调函数就会调用, 得到数据

​ ② 如果先改变的状态, 那当指定回调时, 回调函数就会调用, 得到数据

(4) 个人理解--结合源码

​ 源码中,promise的状态是通过一个默认为padding的变量进行判断,所以当你resolve/reject延时(异步导致当then加载时,状态还未修改)后,这时直接进行p.then()会发现,目前状态还是进行中,所以只是这样导致只有同步操作才能成功.

​ 所以promise将传入的回调函数拷贝到promise对象实例上,然后在resolve/reject的执行过程中再进行调用,达到异步的目的

​ 具体代码实现看下方自定义promise

//html


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Promise关键问题 - Promise 改变状态与指定回调的顺序问题</title>
</head>
<body>
    <script>

        let p = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('OK');
            }, 1000);
        });

        p.then(value => {
            console.log(value);
        },reason=>{
            
        })
    </script>
</body>
</html>
Ⅳ-promise.then()返回的新 promise 的结果状态由什么决定?

(1) 简单表达: 由 then()指定的回调函数执行的结果决定

(2) 详细表达:

​ ① 如果抛出异常, 新 promise 变为 rejected, reason 为抛出的异常

​ ② 如果返回的是非 promise 的任意值, 新 promise 变为 resolved, value 为返回的值

​ ③ 如果返回的是另一个新 promise, 此 promise 的结果就会成为新 promise 的结果

//html

let p = new Promise((resolve, reject) => {
resolve('ok');
});
//执行 then 方法
let result = p.then(value => {
console.log(value);
// 1. 抛出错误 ,变为 rejected
throw '出了问题';
// 2. 返回结果是非 Promise 类型的对象,新 promise 变为 resolved
return 521;
// 3. 返回结果是 Promise 对象,此 promise 的结果就会成为新 promise 的结果
return new Promise((resolve, reject) => {
  // resolve('success');
  reject('error');
});
}, reason => {
console.warn(reason);
});
//html



<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Promise关键问题 - Promise then方法的返回结果特点</title>
</head>
<body>
    <script>
        let p = new Promise((resolve, reject) => {
            resolve('ok');
        });
        //执行 then 方法
        let result = p.then(value => {
            // console.log(value);
            //1. 抛出错误
            // throw '出了问题';
            //2. 返回结果是非 Promise 类型的对象
            // return 521;
            //3. 返回结果是 Promise 对象
            // return new Promise((resolve, reject) => {
            //     // resolve('success');
            //     reject('error');
            // });
        }, reason => {
            console.warn(reason);
        });

        console.log(result);
    </script>
</body>
</html>
Ⅴ- promise 如何串连多个操作任务?

(1) promise 的 then()返回一个新的 promise, 可以开成 then()的链式调用

(2) 通过 then 的链式调用串连多个同步/异步任务,这样就能用then()将多个同步或异步操作串联成一个同步队列

<script>
let p = new Promise((resolve, reject) => { setTimeout(() => {resolve('OK'); }, 1000); });
p.then(value => {return new Promise((resolve, reject) => { resolve("success"); });})
.then(value => {console.log(value);})
.then(value => { console.log(value);})
</script>
//html


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Promise关键问题 - 如何串联多个任务</title>
</head>
<body>
    <script>
        let p = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('OK');
            }, 1000);
        });

        p.then(value => {
            return new Promise((resolve, reject) => {
                resolve("success");
            });
        }).then(value => {
            console.log(value);
        }).then(value => {
            console.log(value);
        })
    </script>
</body>
</html>
Ⅵ-promise 异常传透?
  • 当使用 promise 的 then 链式调用时, 可以在最后指定失败的回调
  • 前面任何操作出了异常, 都会传到最后失败的回调中处理

  • getJSON('./hong.json')
       .then(function(posts) { throw new Error('抛出异常') })
      .then(res=>console.log(res),e=>console.log('被then的错误回调捕获',e) )
       .catch(function(error) {
    		 // 处理 getJSON 和 前一个回调函数运行时发生的错误
     		console.log('错误捕获: ', error);
      });
    //执行结果: 被then的错误回调捕获 Error: 抛出异常
    
    /******************** 利用异常穿****************************************/
    getJSON('./hong.json')
       .then(function(posts) { throw new Error('抛出异常') })
      .then(res=>console.log(res) ) //此处差异,不指定 reject 回调,利用异常穿透传到最后
       .catch(function(error) {
     		console.log('错误捕获: ', error);
      });
    //执行结果:  错误捕获:  Error: 抛出异常

注:可以在每个then()的第二个回调函数中进行err处理,也可以利用异常穿透特性,到最后用catch去承接统一处理,两者一起用时,前者会生效(因为err已经将其处理,就不会再往下穿透)而走不到后面的catch

//html


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Promise关键问题 - 异常穿透</title>
</head>
<body>
    <script>
        let p = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('OK');
                // reject('Err');
            }, 1000);
        });

        p.then(value => {
            // console.log(111);
            throw '失败啦!';
        }).then(value => {
            console.log(222);
        }).then(value => {
            console.log(333);
        }).catch(reason => {
            console.warn(reason);
        });
    </script>
</body>
</html>
Ⅶ- 中断 promise 链?

关键问题2中,可以得知,当promise状态改变时,他的链式调用都会生效,那如果我们有这个一个实际需求:我们有5个then(),但其中有条件判断,如当我符合或者不符合第三个then条件时,要直接中断链式调用,不再走下面的then,该如何?

(1) 当使用 promise 的 then 链式调用时, 在中间中断, 不再调用后面的回调函数

(2) 办法: 在回调函数中返回一个 pendding 状态的promise 对象

<script>
let p = new Promise((resolve, reject) => {setTimeout(() => { resolve('OK');}, 1000);});
p.then(value => {return new Promise(() => {});})//有且只有这一个方式
.then(value => { console.log(222);})
.then(value => { console.log(333);})
.catch(reason => {console.warn(reason);});
</script>
//html


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Promise关键问题 - 中断 Promise 链条</title>
</head>
<body>
    <script>
        let p = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('OK');
            }, 1000);
        });

        p.then(value => {
            console.log(111);
            //有且只有一个方式
            return new Promise(() => {});
        }).then(value => {
            console.log(222);
        }).then(value => {
            console.log(333);
        }).catch(reason => {
            console.warn(reason);
        });
    </script>
</body>
</html>

四、自定义Promise手写

  1. 下方的Promise.prototype.thenPromise.resolve为什么一个挂载在prototype而另一个挂载在实例对象上?

解:原因是分别为静态方法与实例方法

-->上面的需要new实例化的时候自动继承实例prototype上的方法和属性,所以用实例对象.then()来调用,而下面的Promise.resolve是静态方法,不用new,是可以直接Promise.resolve()调用

Ⅰ-Promise的实例方法实现

1 - 初始结构搭建

html引入,该章节后续html大部分重复 除非必要,否则不再放上来

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Promise-封装 | 1 - 初始结构搭建</title>
    <script src="./promise.js"></script>
</head>
<body>
    <script>
        let p = new Promise((resolve, reject) => {
            resolve('OK');
        });
        p.then(value => {
            console.log(value);
        }, reason=>{
            console.warn(reason);
        })
    </script>
</body>
</html>

promise.js -->使用原生写法,最后会改为class写法

function Promise(executor){}
//添加 then 方法
Promise.prototype.then = function(onResolved, onRejected){}

2 - resolve 与 reject构建与基础实现

  1. 使用const self = this;保存this执行,使function中可以取得当前实例

ps:可以不使用该方法保存,但是下方function需要改为箭头函数,否则function默认指向是window

之后代码默认使用self保存this,箭头函数方式将在最后改为class写法时使用

  1. 默认设置 PromiseState = 'pending'以及 PromiseResult = null,这就是promise状态基础
//声明构造函数
function Promise(executor) {
  //添加属性
  this.PromiseState = 'pending';
  this.PromiseResult = null;
  //保存实例对象的 this 的值
/*  此处可以不写,但是下面function方法需要改为箭头函数,否则function默认指向是window */
  const self = this; 
  //resolve 函数
  function resolve(data) {--------------------------------------------
    //1. 修改对象的状态 (promiseState)
    self.PromiseState = 'fulfilled'; // resolved
    //2. 设置对象结果值 (promiseResult)
    self.PromiseResult = data;
  }
  //reject 函数
  function reject(data) {----------------------------------------------
    //1. 修改对象的状态 (promiseState)
    self.PromiseState = 'rejected'; // 
    //2. 设置对象结果值 (promiseResult)
    self.PromiseResult = data;
  }
  //同步调用『执行器函数』
  executor(resolve, reject);
}
//添加 then 方法
Promise.prototype.then = function (onResolved, onRejected) {}
//html


//声明构造函数
function Promise(executor) {
  //添加属性
  this.PromiseState = 'pending';
  this.PromiseResult = null;
  //保存实例对象的 this 的值
/*  此处可以不写,但是下面function方法需要改为箭头函数,否则function默认指向是window */
  const self = this; 
  //resolve 函数
  function resolve(data) {--------------------------------------------
    //1. 修改对象的状态 (promiseState)
    self.PromiseState = 'fulfilled'; // resolved
    //2. 设置对象结果值 (promiseResult)
    self.PromiseResult = data;
  }
  //reject 函数
  function reject(data) {----------------------------------------------
    //1. 修改对象的状态 (promiseState)
    self.PromiseState = 'rejected'; // 
    //2. 设置对象结果值 (promiseResult)
    self.PromiseResult = data;
  }
  //同步调用『执行器函数』
  executor(resolve, reject);
}
//添加 then 方法
Promise.prototype.then = function (onResolved, onRejected) {}

 

//js


//声明构造函数
function Promise(executor){
    //添加属性
    this.PromiseState = 'pending';
    this.PromiseResult = null;
    //保存实例对象的 this 的值
    const self = this;// self _this that
    //resolve 函数
    function resolve(data){
        //1. 修改对象的状态 (promiseState)
        self.PromiseState = 'fulfilled';// resolved
        //2. 设置对象结果值 (promiseResult)
        self.PromiseResult = data;
    }
    //reject 函数
    function reject(data){
        //1. 修改对象的状态 (promiseState)
        self.PromiseState = 'rejected';// 
        //2. 设置对象结果值 (promiseResult)
        self.PromiseResult = data;
    }

    //同步调用『执行器函数』
    executor(resolve, reject);
}

//添加 then 方法
Promise.prototype.then = function(onResolved, onRejected){

}

3 - throw 抛出异常改变状态

  1. 在2的基础上进行修改:将执行器放入try-catch()
  2. 在catch中使用reject()修改 promise 对象状态为『失败

 try {
    //同步调用『执行器函数』
    executor(resolve, reject);
  } catch (e) {
    //修改 promise 对象状态为『失败』
    reject(e);
  }

//js

//声明构造函数
function Promise(executor){
    //添加属性
    this.PromiseState = 'pending';
    this.PromiseResult = null;
    //保存实例对象的 this 的值
    const self = this;// self _this that
    //resolve 函数
    function resolve(data){
        //1. 修改对象的状态 (promiseState)
        self.PromiseState = 'fulfilled';// resolved
        //2. 设置对象结果值 (promiseResult)
        self.PromiseResult = data;
    }
    //reject 函数
    function reject(data){
        //1. 修改对象的状态 (promiseState)
        self.PromiseState = 'rejected';// 
        //2. 设置对象结果值 (promiseResult)
        self.PromiseResult = data;
    }
    try{
        //同步调用『执行器函数』
        executor(resolve, reject);
    }catch(e){
        //修改 promise 对象状态为『失败』
        reject(e);
    }
}

//添加 then 方法
Promise.prototype.then = function(onResolved, onRejected){

}

4 - 状态只能修改一次

  1. 基于2 3代码中resolve和reject方法进修改

  2. 在成功与失败函数中添加判断 if(self.PromiseState !== 'pending') return;,如果进入函数时状态不为pending直接退出,这样就能做到状态只能从pending改至其他状态且做到只能改一次

html调用--------------------------------------------------------
 let p = new Promise((resolve, reject) => {
      reject("error");
      resolve('OK');
      //抛出异常
      // throw "error";
    });
 console.log(p);
promise.js修改--------------------------------------------------------

  //resolve 函数
    function resolve(data){
        //判断状态
        if(self.PromiseState !== 'pending') return;
        //1. 修改对象的状态 (promiseState)
        self.PromiseState = 'fulfilled';// resolved
        //2. 设置对象结果值 (promiseResult)
        self.PromiseResult = data;
    }
    //reject 函数
    function reject(data){
        //判断状态
        if(self.PromiseState !== 'pending') return;
        //1. 修改对象的状态 (promiseState)
        self.PromiseState = 'rejected';// 
        //2. 设置对象结果值 (promiseResult)
        self.PromiseResult = data;
    }
//html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Promise-封装 | 5 - 状态只能修改一次 </title>
    <script src="./promise.js"></script>
</head>
<body>
    <script>
        let p = new Promise((resolve, reject) => {
            reject("error");
            resolve('OK');
            //抛出异常
            // throw "error";
        });

        console.log(p);
    </script>
</body>
</html>
//js


//声明构造函数
function Promise(executor){
    //添加属性
    this.PromiseState = 'pending';
    this.PromiseResult = null;
    //保存实例对象的 this 的值
    const self = this;// self _this that
    //resolve 函数
    function resolve(data){
        //判断状态
        if(self.PromiseState !== 'pending') return;
        //1. 修改对象的状态 (promiseState)
        self.PromiseState = 'fulfilled';// resolved
        //2. 设置对象结果值 (promiseResult)
        self.PromiseResult = data;
    }
    //reject 函数
    function reject(data){
        //判断状态
        if(self.PromiseState !== 'pending') return;
        //1. 修改对象的状态 (promiseState)
        self.PromiseState = 'rejected';// 
        //2. 设置对象结果值 (promiseResult)
        self.PromiseResult = data;
    }
    try{
        //同步调用『执行器函数』
        executor(resolve, reject);
    }catch(e){
        //修改 promise 对象状态为『失败』
        reject(e);
    }
}

//添加 then 方法
Promise.prototype.then = function(onResolved, onRejected){

}

5 - then 方法执行回调基础实现

  1. 修改Promise.prototype.then方法
  2. 传入then(成功回调,失败回调),当调用then后,会判断当前this.PromiseState的状态,当其为成功时调用成功回调,失败时调用失败回调

html调用------------------------------------------------------------
    let p = new Promise((resolve, reject) => {
      // resolve('OK');// reject("Error");
      throw "ERROR";
    });
    p.then(
        value => {console.log(value); }, 
        reason => {console.warn(reason);}
    )
promise.js修改与实现-----------------------------------------------------
//添加 then 方法
Promise.prototype.then = function (onResolved, onRejected) {
  //调用回调函数  PromiseState
  if (this.PromiseState === 'fulfilled') {onResolved(this.PromiseResult);}
  if (this.PromiseState === 'rejected') {onRejected(this.PromiseResult);}
}

//htmml


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Promise-封装 | 6 - then 方法执行回调 </title>
    <script src="./promise.js"></script>
</head>
<body>
    <script>
        //实例化对象
        let p = new Promise((resolve, reject) => {
            // resolve('OK');
            // reject("Error");
            throw "ERROR";
        });

        p.then(value => {
            console.log(value);
        }, reason=>{
            console.warn(reason);
        })
    </script>
</body>
</html>
//js


//声明构造函数
function Promise(executor){
    //添加属性
    this.PromiseState = 'pending';
    this.PromiseResult = null;
    //保存实例对象的 this 的值
    const self = this;// self _this that
    //resolve 函数
    function resolve(data){
        //判断状态
        if(self.PromiseState !== 'pending') return;
        //1. 修改对象的状态 (promiseState)
        self.PromiseState = 'fulfilled';// resolved
        //2. 设置对象结果值 (promiseResult)
        self.PromiseResult = data;
    }
    //reject 函数
    function reject(data){
        //判断状态
        if(self.PromiseState !== 'pending') return;
        //1. 修改对象的状态 (promiseState)
        self.PromiseState = 'rejected';// 
        //2. 设置对象结果值 (promiseResult)
        self.PromiseResult = data;
    }
    try{
        //同步调用『执行器函数』
        executor(resolve, reject);
    }catch(e){
        //修改 promise 对象状态为『失败』
        reject(e);
    }
}

//添加 then 方法
Promise.prototype.then = function(onResolved, onRejected){
    //调用回调函数  PromiseState
    if(this.PromiseState === 'fulfilled'){
        onResolved(this.PromiseResult);
    }
    if(this.PromiseState === 'rejected'){
        onRejected(this.PromiseResult);
    }
}

6 - 异步任务 then 方法实现

  1. 此处对于5有四处修改,下面上js代码

  2. 当我运行异步代码后,我的执行器内部代码还未返回(因为用了定时器,里面的代码进入了异步队列),所以当我下面的.then()运行时:我的ppending状态,所以根本不会执行resolve与reject方法

解:添加判断pending状态,将当前回调函数保存到实例对象(存到实例上是为了更方便)中,这样后续改变状态时候才调用得到

  1. 为什么要将回调保存到实例上而不是直接调用?

理由:因为我的回调函数需要在我的promise状态改变后(成功或者失败),再根据状态选择运行哪个函数 所以当你调用then()时却检测到状态为pending,说明这时候的promise在异步队列 不能直接运行成功或者失败函数

解决:因为resolve与reject方法与then()不在同一个作用域中,并不能共享then(成功回调,失败回调)的参数,所以在判断状态为pending时将回调保存到实例对象上.然后将回调函数的调用放在resolve()与reject()

这样当我代码运行到异步队列的resolve()或reject()时,就可以在这个函数中运行回调函数,实现异步then

  1. 此处的then仍有瑕疵,需要继续完善
html调用------------------------------------------------------------
 //实例化对象
    let p = new Promise((resolve, reject) => {
      setTimeout(() => {reject("error"); /* resolve('OK');*/}, 1000);
    });
    p.then(value => {console.log(value);},reason => { console.warn(reason);});
    console.log(p);

promise.js修改与实现-----------------------------------------------------
//声明构造函数
function Promise(executor) {
  this.PromiseState = 'pending'; this.PromiseResult = null;
  // 声明属性     
  this.callback = {};			-----------新添加1
  const self = this; 
    
  //resolve 函数
  function resolve(data) {
    //判断状态
    if (self.PromiseState !== 'pending') return;
    self.PromiseState = 'fulfilled'; self.PromiseResult = data;
    //调用成功的回调函数  加判断的原因是防止无回调报错
    if (self.callback.onResolved) { self.callback.onResolved(data); }  ------------新添加2 最重要 
  }
    
  //reject 函数
  function reject(data) {
    if (self.PromiseState !== 'pending') return;
    self.PromiseState = 'rejected'; self.PromiseResult = data;
    //执行回调						
    if (self.callback.onResolved) { self.callback.onResolved(data);}  ------------新添加3
  }
  try {executor(resolve, reject);} catch (e) {reject(e);}
}

//添加 then 方法
Promise.prototype.then = function (onResolved, onRejected) {
  //调用回调函数  PromiseState
  if (this.PromiseState === 'fulfilled') {onResolved(this.PromiseResult);}
  if (this.PromiseState === 'rejected') { onRejected(this.PromiseResult);}
  //判断 pending 状态
  if (this.PromiseState === 'pending') {  ------------新添加4
    //保存回调函数
    this.callback = {
      onResolved: onResolved,
      onRejected: onRejected
    }
  }
}

7 - 指定多个回调

  1. 基于6代码进行修改 只展示修改部分代码

  2. 6中保存回调函数的方式有BUG,如果我有多个.then(),后面加载的回调函数会覆盖之前的回调函数,导致最后回调函数有且只有最后一个

解:使用数组的方式进行存储回调函数,调用时也是用数组循环取出

  1. 此处的then仍有瑕疵,需要继续完善
html调用------------------------------------------------------------
//实例化对象
   let p = new Promise((resolve, reject) => {setTimeout(() => {reject('No');}, 1000);});
   p.then(value => { console.log(value);}, reason=>{console.warn(reason);});
   p.then(value => { alert(value);}, reason=>{ alert(reason);});
   console.log(p);

promise.js修改与实现-----------------------------------------------------
Promise.prototype.then = function (onResolved, onRejected) {
       //resolve 函数
    function resolve(data){
  		.....
        //调用成功的回调函数
        // if (self.callback.onResolved) { self.callback.onResolved(data); } 
        self.callbacks.forEach(item => {   --------修改1
            item.onResolved(data);
        });
    }
    //reject 函数
    function reject(data){
     	 ......
        //执行失败的回调
        // if (self.callback.onResolved) { self.callback.onResolved(data);}
        self.callbacks.forEach(item => {		------修改2
            item.onRejected(data);
        });
    }
    
  //添加 then 方法
Promise.prototype.then = function(onResolved, onRejected){
    ........
    //判断 pending 状态
    if(this.PromiseState === 'pending'){
        //保存回调函数
        //  this.callback = { onResolved: onResolved, onRejected: onRejected }
        this.callbacks.push({					--------修改3
            onResolved: onResolved,
            onRejected: onRejected
        });
    }
}
//html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Promise-封装 | 8 - 指定多个回调 </title>
    <script src="./promise.js"></script>
</head>
<body>
    <script>
        //实例化对象
        let p = new Promise((resolve, reject) => {
            setTimeout(() => {
                // resolve('OK');
                reject('No');
            }, 1000);
        });

        p.then(value => {
            console.log(value);
        }, reason=>{
            console.warn(reason);
        });

        p.then(value => {
            alert(value);
        }, reason=>{
            alert(reason);
        });

        console.log(p);


    </script>
</body>
</html>
//js


//声明构造函数
function Promise(executor){
    //添加属性
    this.PromiseState = 'pending';
    this.PromiseResult = null;
    //声明属性
    this.callbacks = [];
    //保存实例对象的 this 的值
    const self = this;// self _this that
    //resolve 函数
    function resolve(data){
        //判断状态
        if(self.PromiseState !== 'pending') return;
        //1. 修改对象的状态 (promiseState)
        self.PromiseState = 'fulfilled';// resolved
        //2. 设置对象结果值 (promiseResult)
        self.PromiseResult = data;
        //调用成功的回调函数
        self.callbacks.forEach(item => {
            item.onResolved(data);
        });
    }
    //reject 函数
    function reject(data){
        //判断状态
        if(self.PromiseState !== 'pending') return;
        //1. 修改对象的状态 (promiseState)
        self.PromiseState = 'rejected';// 
        //2. 设置对象结果值 (promiseResult)
        self.PromiseResult = data;
        //执行失败的回调
        self.callbacks.forEach(item => {
            item.onRejected(data);
        });
    }
    try{
        //同步调用『执行器函数』
        executor(resolve, reject);
    }catch(e){
        //修改 promise 对象状态为『失败』
        reject(e);
    }
}

//添加 then 方法
Promise.prototype.then = function(onResolved, onRejected){
    //调用回调函数  PromiseState
    if(this.PromiseState === 'fulfilled'){
        onResolved(this.PromiseResult);
    }
    if(this.PromiseState === 'rejected'){
        onRejected(this.PromiseResult);
    }
    //判断 pending 状态
    if(this.PromiseState === 'pending'){
        //保存回调函数
        this.callbacks.push({
            onResolved: onResolved,
            onRejected: onRejected
        });
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值