超详细 Promise 介绍,优雅地实现异步任务,手撕 Promise

Promise

1. Promise 的理解和使用

✨Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了 Promise 对象。

所谓 Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

🎗️Promise 对象有以下两个特点:

1️⃣ 对象的状态不受外界影响。Promise 对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是**“承诺”**,表示其他手段无法改变。

2️⃣ 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

🎗️Promise 的缺点:

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

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

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

1.1 Promise 是什么?

抽象表达:

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

2)Promise 是 JS 中进行异步编程的新解决方案(备注:旧方案是单纯使用回调函数)

具体表达:

1)从语法上来说: Promise 是一个构造函数

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

1.1.1 Promise 的使用

① Promise 封装计时器
// Promise 实现
const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        let n = rand(1, 100)
        if (n <= 30) {
            resolve(n);
        } else {
            reject(n);
        }
    }, 1000)
})

// 调用 then 方法
promise.then(
    // [成功]状态
    (value) => {
        alert("恭喜中奖了,号码为 " + value)
    },
    // [失败]状态
    (reason) => {
        alert("很遗憾,再接再厉,号码为 " + reason)
    }
)
② Promise 封装文件操作(fs)
// Promise 形式
let p = new Promise((resolve, reject) => {
    fs.readFile('./resource/content.txt', (err, data) => {
        // 失败
        if (err) reject(err);
        // 成功
        resolve(data);
    })
})

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

-----------------------------------------------------------------------

/**
 * 封装一个函数 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
const btn = document.querySelector('#btn');

btn.addEventListener('click', function () {
    const p = new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open('GET', 'http://localhost:8000/promise');
        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)
    })
});

-----------------------------------------------------------------------

/**
 * 封装一个函数 sendAJAX 发送 GET AJAX 请求
 * 参数:  URL
 * 返回:  promise 对象
 */
function sendAJAX(url) {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        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('http://localhost:8000/promise').then(value => {
    console.log(value);
}, reason => {
    console.warn(reason);
});
④ util.promisify方法

可以将函数直接变成 promise 的封装方式,不用再去手动封装

// 引入 util 模块
const util = require("util");
// 引入 fs 模块
const fs = require("fs");
// 返回一个新的函数
let mineReadFile = util.promisify(fs.readFile);

mineReadFile('./resource/content.txt').then(value => {
    console.log(value.toString());
}, err => {
    console.log(err);
})

1.1.2 Promise 的状态

1)promise 的状态

实例对象中的一个属性 『PromiseState』

  • pending 未决定的
  • resolved / fulfilled成功
  • rejected 失败
2)promise 状态改变
  1. pending 变为 resolved
  2. pending 变为 rejected

说明: 只有这 2 种, 且一个 promise 对象只能改变一次 无论变为成功还是失败, 都会有一个结果数据 成功的结果数据一般称为 value, 失败的结果数据一般称为 reason

3)promise 对象的值

实例对象中的一个属性 『PromiseResult』

保存着异步任务『成功/失败』的结果

reject(err);	// PromiseResult 为 err 值

resolve(data);	// PromiseResult 为 data 值

// 通过对 promise 对象使用 then 方法拿到『成功/失败』的结果
xx.then(value => {}, reason => {})

1.1.3 Promise 的基本流程

在这里插入图片描述

在这里插入图片描述

1.2 为什么要用 Promise ?

① 指定回调函数的方式更加灵活

  1. 旧的: 必须在启动异步任务前指定
  2. promise: 启动异步任务 => 返回promie对象 => 给promise对象绑定回调函 数(甚至可以在异步任务结束后指定/多个)

② 支持链式调用, 可以解决回调地狱问题

🎗️回调地狱:

回调函数嵌套调用, 外部回调函数异步执行的结果是嵌套的回调执行的条件

在这里插入图片描述

🎗️解决方案:

promise 链式调用

new Promise((resolve, reject) => {
    resolve('成功了')
})
    .then(
        (data) => { console.log('onResolved1', data); return data;},
        (error) => { console.log('onRejected1', error); return error;}
    )
    .then(
        (data) => { console.log('onResolved2', data); },
        (error) => { console.log('onRejected2', error); }
    )

但是只是简单的改变格式,并没有彻底解决上面的问题真正要解决上述问题,一定要利用 promise 再加上 await 和 async 关键字实现异步传同步

async function fetchDataAndSave() {
    try {
        const data = await fetchData();
        const processedData = await processData(data);
        await saveData(processedData);
        console.log("Data saved successfully!");
    } catch (error) {
        console.error("An error occurred:", error);
    }
}

1.3 Promise 常用 API

① Promise 构造函数:Promise (excutor){}

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

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

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

说明:executor 会在 Promise 内部立即同步调用,异步操作在执行器中执行。

let p = new Promise((resolve, reject) => {
    //同步调用,立即执行
    resolve('OK');
})
//再调用外部

② 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)。

let p = new Promise((resolve, reject) => {
    //修改promise对象状态
    reject('error');
})

//执行catch方法
p.catch(reason => {
    console.log(reason);
})

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

异常穿透:不执行 then,直接执行 catch

在 .then() 中,我们经常传入 onResolved 用以处理成功时的数据,一般不在 then 里面传入 onRejected,而处理失败的数据一般放在最后的 .catch() 中:

// 现在执行失败:
new Promise((resolve, reject) => {
  reject('失败了')
}).then(
  (data) => { console.log('onResolved1', data)},
).then(
  (data) => { console.log('onResolved2', data)},
).then(
  (data) => { console.log('onResolved3', data)},
).catch(
  (err) => { console.log('onRejected1', data)},
)

这个失败的数据是一下子就直接进入了.catch吗?显然不是的,既然是链式调用,那必定也是一层层的传过去了,但是在 .then() 中并没有传入处理失败数据的回调,默认是这样的:

.then(
(data) => { ...处理data },
// 不写第二个参数, 相当于默认传了:
(err) => Promise.reject(err), 
// 或
(err) => { throw err; }
).then()

then 链式调用中不传入 onRejected(即没有传递错误处理回调),如果在任何 then 的成功回调中出现错误,这个错误会沿着 then 链向后传播,直到遇到第一个 catch

另一种情况:

let p = new Promise((resolve, reject) => {
	reject("error!!!")
})
p.then(res => {console.log(111)},res=>{console.log("rej1")})
    .then(res => {console.log(222) },res=>{console.log("rej2")})
    .catch(err => {console.log(err)})

执行结果:
rej1
222

由于 p 被 rejected,then 的第二个回调(即 res => {console.log("rej1")})会执行并输出 rej1,由于该回调函数处理了错误,所以 catch 不会处理该错误。

第一个 then 返回了一个新的 Promise,它的状态为 fulfilled,因为前面的 rejected 状态被第一个 then 的第二个回调捕获了,成功进行了处理。所以接下来执行第二个 then 的第一个回调(即 res => {console.log(222)})并输出 222

最后,由于错误已被第一个 then 处理完,没有错误流传到 .catch,因此 .catch 不会执行。

④ Promise.resolve方法: (value) =>{}

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

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

//如果传入参数为非 promise 类型的对象,返回的结果为成功 promise 对象
let p = Promise.resolve(114);
console.log(p);

在这里插入图片描述

//如果传入的参数为 promise 对象,则参数的结果决定了 resolve 的结果
let pc = Promise.resolve(new Promise((resolve, reject) => {
    resolve('OK');	// 传入的 Promise 状态为 fulfilled,返回的 Promise 也为 fulfilled
}))
console.log(pc);

在这里插入图片描述

let pc = Promise.resolve(new Promise((resolve, reject) => {
    // resolve('OK');
    reject('error');  // 传入的 Promise 状态为 rejected,返回的 Promise 也为 rejected
}))
console.log(pc);

在这里插入图片描述

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

(1)reason:失败的原因

说明:返回一个失败的 promise 对象

无论传入什么数值,都返回失败的结果(包括传入成功的 resolve 的 promise 对象)。

⑥ Promise.all方法:(promises)=> {}

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

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

Promise.all() 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

const p = Promise.all([p1, p2, p3]);

p的状态由p1p2p3决定,分成两种情况。

  1. 只有p1p2p3的状态都变成fulfilledp的状态才会变成fulfilled,此时p1p2p3的返回值组成一个数组,传递给p的回调函数。
  2. 只要p1p2p3之中有一个被rejectedp的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

⑦ Promise.race 方法: (promises)=>{}

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

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

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);

执行结果:
Success

1.4 Promise 的几个关键问题

1.4.1 如何改变 promise 的状态?

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

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

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

let p = new Promise((resolve, reject) => {
    // 1. resolve 函数
    resolve('ok');      // pending => fulfilled

    // 2. reject 函数
    reject('error');    // pending => rejected

    // 3. throw
    throw 'error !!!';  // pending => rejected
});

1.4.2 一个 promise 指定多个成功/失败回调函数, 都会调用吗?

当 promise 改变为对应状态时都会调用,改变状态后,多个回调函数都会调用,并不会自动停止。

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

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

// 指定回调-2
p.then(value => {console.log(value + '2')});

执行结果:
OK1
OK2

1.4.3 改变 promise 状态和指定回调函数谁先谁后?

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

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

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

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

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

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

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

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

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

let p = new Promise((resolve, reject) => {
    //异步写法,这样写会先指定回调,再改变状态
    setTimeout(() => {resolve('OK'); }, 1000);
    
    //这是同步写法,这样写会先改变状态,再指定回调
    resolve('OK'); 
});
p.then(value => {console.log(value);}, reason => {})

1.4.4 promise.then() 返回的新 promise 的结果状态由什么决定?

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

(2)详细表达:

  • 如果抛出异常,新 promise 变为 rejected,reason 为抛出的异常。

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

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

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);
});

1.4.5 promise 如何串连多个操作任务?

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

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

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);})	// 输出 success
    .then(value => { console.log(value);})	// 输出 undefined

1.4.6 promise 异常传透?

  • 当使用 promise 的 then 链式调用时,可以在最后指定失败的回调
  • 前面任何操作出了异常,都会传到最后失败的回调中处理
new Promise((resolve, reject) => {
  ...						// 异步操作 1
}).then(
  (value) => { ... },		// 异步操作 2
).then(
  (value) => { ... },		// 异步操作 3
).then(
  (value) => { ... },		// 异步操作 4
).catch(
  (err) => { ... }			// 任何一个异步操作出错,直接执行 catch 操作
)

1.4.7 中断 promise 链?

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

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

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

p.then(value => {
    console.log('111');

    // 唯一中断方式:返回一个 pending  状态的 Promise 对象
    return new Promise(() => {});
}).then(value => {
    console.log('222');
}).then(value => {
    console.log('333');
}).catch(reason => {
    console.warn(reason);
});

执行结果:
111

2. 自定义(手写)Promise

手撕源码,不深入的话可跳过

2.1 Promise 的实例方法实现

2.1.1 初始结构搭建

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>index</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

// 声明构造函数
function Promise(executor) {

}

// 添加 then 方法
Promise.prototype.then = function (onResolve, onReject) {

};

2.1.2 resolve 与 reject 构建与实现

  1. 使用const self = this;保存this执行,使function中可以取得当前实例(或用箭头函数)
  2. 默认设置 PromiseState = 'pending'以及 PromiseResult = null,这就是promise状态基础
// 声明构造函数
function Promise(executor) {
    // 添加属性
    this.PromiseState = 'pending';
    this.PromiseResult = null;

    // 保存实例对象的 this 指向
    const self = this;

    // resolve 函数
    function resolve(data) {
        // 1.修改对象的状态(PromiseState)
        self.PromiseState = 'fulfilled';
        // 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 (onResolve, onReject) {

};

2.1.3 throw 抛出异常改变状态

  1. 在 2 的基础上进行修改:将执行器放入try-catch()
  2. 在 catch 中使用reject()修改 promise 对象状态为『失败
try {
    // 同步调用『执行器函数』
    executor(resolve, reject);
} catch (e) {
    // 修改 promise 对象状态为『失败』
    reject(e);
}

2.1.4 状态只能修改一次

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

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

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

2.1.5 then 方法执行回调基础实现

  1. 修改 Promise.prototype.then 方法
  2. 传入 then(成功回调,失败回调),当调用then后,会判断当前 this.PromiseState 的状态,当其为成功时调用成功回调,失败时调用失败回调
// 添加 then 方法
Promise.prototype.then = function (onResolved, onRejected) {
    if (this.PromiseState === 'fulfilled') {
        onResolved(this.PromiseResult);
    }

    if (this.PromiseState === 'rejected') {
        onRejected(this.PromiseResult);
    }
};

2.1.6 异步任务 then 方法实现

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

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

p.then(value => {
    console.log(value);
}, reason => {
    console.warn(reason);
})
promise.js ---------------------------------------------------------------------
// 声明构造函数
function Promise(executor) {
    // 添加属性
    this.PromiseState = 'pending';
    this.PromiseResult = null;
    this.callback = {};		-----------------------------------------

    // 保存实例对象的 this 指向
    const self = this;

    // resolve 函数
    function resolve(data) {
        // 判断状态
        if (self.PromiseState !== 'pending') return;
        // 1.修改对象的状态(PromiseState)
        self.PromiseState = 'fulfilled';
        // 2.设置对象结果值(PromiseResult)
        self.PromiseResult = data;
        // 调用成功的回调
        if (self.callback.onResolved) {	-----------------------------------------
            self.callback.onResolved(data);
        }
    }

    // reject 函数
    function reject(data) {
        // 判断状态
        if (self.PromiseState !== 'pending') return;
        // 1.修改对象的状态(PromiseState)
        self.PromiseState = 'rejected';
        // 2.设置对象结果值(PromiseResult)
        self.PromiseResult = data;
        // 调用失败的回调
        if (self.callback.onRejected) {	-----------------------------------------
            self.callback.onRejected(data);
        }
    }

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

}

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

2.1.7 指定多个回调

当对 p 使用两个 then 回调时,只执行了后面那个回调的行为,这是由于两个 then 是同步的,而内部是使用对象保存回调函数,后面加载的回调函数会覆盖之前的回调函数,导致最后回调函数 有且只有 最后一个。

在这里插入图片描述

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

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

// 只执行了这个回调
p.then(value => {
    alert(value)
}, reason => {
    alert(reason)
})

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

在这里插入图片描述

// 声明构造函数
function Promise(executor) {
    // 添加属性
	......
    this.callbacks = [];		-----------------------------------------
    
    ......

    // resolve 函数
    function resolve(data) {
        ......
        // 调用成功的回调
        self.callbacks.forEach(item => { -----------------------------------------
            item.onResolved(data);
        });
    }

    // reject 函数
    function reject(data) {
        ......
        self.callbacks.forEach(item => { -----------------------------------------
            item.onRejected(data);
        });
    }
}

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

2.1.8 同步任务 then 返回结果

  1. 由前面的 then 运行结果中得知,我们使用 then 后的返回结果是其回调函数的返回结果,而我们需要的返回结果是一个新的 promise 对象

    🎗️所以在then中 return new Promise(),使其得到的是一个新的 promise 对象

  2. 新的promise对象因为没有用 rejerect与resolve 方法,导致返回的状态一直是 pending

    🎗️在新的 promise 中判断 运行回调函数 后的返回值是什么,然后根据其不同类型给其赋予不同状态

    if(result instanceof Promise):如果回调函数的返回值是一个新的promise对象,调用该promise对象的 then 回调函数,使用 resolve 和 reject 方法,将其自身的状态赋给外层promise

    ② 如果返回值是一个 非promise 对象,返回状态设置为成功,结果为该值

    ③ 如果返回值是一个异常,返回状态设置为失败

在这里插入图片描述

// 声明构造函数
function Promise(executor) {
	// 处理异常
    try {
        // 同步调用『执行器函数』
        executor(resolve, reject);
    } catch (e) {
        // 修改 promise 对象状态为『失败』
        reject(e);
    }

}

// 添加 then 方法
Promise.prototype.then = function (onResolved, onRejected) {
    return new Promise((resolve, reject) => {
        if (this.PromiseState === 'fulfilled') {
            // 获取回调函数执行结果
            let result = onResolved(this.PromiseResult);
            if (result instanceof Promise) {
                // 如果是 Promise 对象,将该对象的状态和值传递给外层
                result.then(v => {
                    resolve(v);
                }, r => {
                    reject(r);
                });
            } else {
                // 结果的对象状态为『成功』
                resolve(result);
            }
        }
        if (this.PromiseState === 'rejected') {
            // 获取回调函数执行结果
            let result = onRejected(this.PromiseResult);
            if (result instanceof Promise) {
                // 如果是 Promise 对象,将该对象的状态和值传递给外层
                result.then(v => {
                    resolve(v);
                }, r => {
                    reject(r);
                });
            } else {
                // 结果的对象状态为『失败』
                reject(result);
            }
        }
        //判断 pending 状态
        if (this.PromiseState === 'pending') {
            //保存回调函数
            this.callbacks.push({
                onResolved: onResolved,
                onRejected: onRejected,
            });
        }
    });
};

2.1.9 异步任务 then 返回结果

要在一个异步任务后正确的返回结果(异步任务执行完,对应的返回结果也顺利出现)

① 声明一个新的函数:其内部功能 -> 先运行 onResolved 回调函数,再将其返回值取出,进行判断其返回值修改外层状态和值(这个过程同 2.1.8

② 加工后存入实例回调函数数组,之后在 resolve 与 reject 方法中调用即可

原理:当异步任务执行时,同步的 Promise 对象状态为 pending,异步任务完成后会调用 resolve 或 reject 方法,然后在这个时机将同步的 Promise 改状态和值(运行新函数),再正确返回。

index.html ---------------------------------------------------------------------
//实例化对象
let p = new Promise((resolve, reject) => {
    setTimeout(() => {reject("Error");}, 1000)}); // resolve('OK');
//执行 then 方法
const res = p.then(value => {
    // return 'oh Yeah';  //如果有返回,根据其返回值得到相应的状态:字符串为成功,抛出为错误
    throw 'error';
}, reason => {
    console.warn(reason, "xx"); //如果只是打印没返回,则实际上时返回一个undefined,
    //在我们封装js中,undefined会判定为非promise对象,所以状态为成功,结果为undefined
    return "sss"   // throw 'error';
});
console.log(res);
promise.js ---------------------------------------------------------------------
// 声明构造函数
function Promise(executor) {
    // 添加属性
    this.PromiseState = 'pending';
    this.PromiseResult = null;
    this.callbacks = [];

    // 保存实例对象的 this 指向
    const self = this;

    // resolve 函数
    function resolve(data) {
        // 判断状态
        if (self.PromiseState !== 'pending') return;
        // 1.修改对象的状态(PromiseState)
        self.PromiseState = 'fulfilled';
        // 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) {
    const self = this;
    return new Promise((resolve, reject) => {
        if (this.PromiseState === 'fulfilled') {
            // 获取回调函数执行结果
            let result = onResolved(this.PromiseResult);
            if (result instanceof Promise) {
                // 如果是 Promise 对象,将该对象的状态和值传递给外层
                result.then(v => {
                    resolve(v);
                }, r => {
                    reject(r);
                });
            } else {
                // 结果的对象状态为『成功』
                resolve(result);
            }
        }
        if (this.PromiseState === 'rejected') {
            // 获取回调函数执行结果
            let result = onRejected(this.PromiseResult);
            if (result instanceof Promise) {
                // 如果是 Promise 对象,将该对象的状态和值传递给外层
                result.then(v => {
                    resolve(v);
                }, r => {
                    reject(r);
                });
            } else {
                // 结果的对象状态为『失败』
                reject(result);
            }
        }
        // 判断 pending 状态
        if (this.PromiseState === 'pending') {
            // 保存回调函数
            this.callbacks.push({
                // 通过该函数可以在异步任务结束时通过 resolve 被调用,改变外层 Promise 对象
                onResolved: function () { ------------------------------------------
                    try {
                        // 执行成功回调函数
                        let result = onResolved(self.PromiseResult);
                        if (result instanceof Promise) {
                            result.then(
                                v => { resolve(v); },
                                r => { reject(r); }
                            );
                        } else { resolve(result); }
                    } catch (e) { reject(e); }

                },
                // 通过该函数可以在异步任务结束时通过 reject 被调用,改变外层 Promise 对象
                onRejected: function () { ------------------------------------------
                    try {
                        // 执行失败回调函数
                        let result = onRejected(self.PromiseResult);
                        if (result instanceof Promise) {
                            result.then(
                                v => { resolve(v); },
                                r => { reject(r); }
                            );
                        } else { resolve(result); }
                    } catch (e) { reject(e); }
                }
            });
        }
    });
};

2.1.10 then 方法代码优化

在8、9、10中可以看出,其判断与改变返回结果状态的代码块是基本重复的,所以可以将其抽出

// 添加 then 方法
Promise.prototype.then = function (onResolved, onRejected) {
    const self = this;
    return new Promise((resolve, reject) => {
        // 封装函数 -------------------------------------------------------------
        function callback(type) {
            try {
                // 获取回调函数执行结果
                let result = type(self.PromiseResult);
                if (result instanceof Promise) {
                    // 如果是 Promise 对象,将该对象的状态和值传递给外层
                    result.then(v => {
                        resolve(v);
                    }, r => {
                        reject(r);
                    });
                } else {
                    // 非Promise结果,状态为『成功』
                    resolve(result);
                }
            } catch (e) {
                reject(e);
            }
        }

        if (this.PromiseState === 'fulfilled') {
            callback(onResolved); ---------------------------------------------
        }
        if (this.PromiseState === 'rejected') {
            callback(onRejected); ---------------------------------------------
        }
        //判断 pending 状态
        if (this.PromiseState === 'pending') {
            //保存回调函数
            this.callbacks.push({
                onResolved: function () {
                    callback(onResolved); ----------------------------------------
                },
                onRejected: function () {
                    callback(onRejected); -----------------------------------------
                }
            });
        }
    });
};

2.1.11 catch 方法与异常穿透与值传递

实现 .then(…).then(…)…catch(…)

index.html ------------------------------------------------------------  
//实例化对象
    let p = new Promise((resolve, reject) => {
      setTimeout(() => {resolve('OK'); }, 2000);
    });
    //值传递
    p.then()
    .then(value => {console.log(222);})
      .then(value => {console.log(333);})
        .catch(reason => {console.warn(reason);});
promise.js ------------------------------------------------------------  
// 添加 then 方法
Promise.prototype.then = function (onResolved, onRejected) {
	...
  if (typeof onRejected !== 'function') { onRejected = reason => { throw reason; } }
  if (typeof onResolved !== 'function') { onResolved = value => value; }
	...
}
// 添加 catch 方法  
Promise.prototype.catch = function(onRejected){
    return this.then(undefined, onRejected);
}

2.2 Promise 的静态方法实现

2.2.1 Promise.resolve 封装

  1. 判断传入的参数是否为promise对象

    • 如果为promise:将其状态与结果赋值给外层 promise 对象

    • 如果为非promise:状态设置为成功

// 添加 resolve 方法
Promise.resolve = function (value) {
    // 返回 Promise 对象
    return new Promise((resolve, reject) => {
        // Promise 对象则向外层传递状态的值
        if (value instanceof Promise) {
            value.then(v => {
                resolve(v);
            }, r => {
                reject(r);
            });
        } else {
            // 非 Promise 对象,成功
            resolve(value);
        }
    });
};

2.2.2 Promise.reject 封装

不同于resolve,这个方法只要把传入参数再次传出去,并将状态改为失败即可

// 添加 reject 方法
Promise.reject = function (reason) {
    return new Promise((resolve, reject) => {
        reject(reason);
    })
}

2.2.3 Promise.all 封装

  1. 遍历传入的 promise 数组,每当遍历结果是成功,则用计数器记录,当计数器等同于数组长度,则全部成功,这时候可以返回成功状态

  2. 如果当数组中任意一个 promise 的执行结果是 reject,直接中断,返回状态为失败

index.html -------------------------------------------------------------------------
let p1 = new Promise((resolve, reject) => {
    setTimeout(() => {resolve('OK'); }, 1000)
})
let p2 = Promise.reject('Success');
let p3 = Promise.resolve('Oh Yeah');
//调用 all 方法
let result = Promise.all([p1, p2, p3]);
console.log(result);
promise.js -------------------------------------------------------------------------
// 添加 all 方法
Promise.all = function (promises) {
    return new Promise((resolve, reject) => {
        // 声明变量
        let count = 0;
        let arr = [];
        // 遍历
        for (let i = 0; i < promises.length; i++) {
            promises[i].then(v => {
                // 记录里面成功的promise对象数量
                count++;
                // 将当前 promise 对象成功的结果存到数组中
                arr[i] = v;
                if (count === promises.length) {
                    resolve(arr);
                }
            }, r => {
                reject(r);
            });
        }
    });
};

2.2.4 Promise.race 封装

直接谁先执行就返回谁的运行结果即可

index.html -------------------------------------------------------------------------
 let p1 = new Promise((resolve, reject) => {
      setTimeout(() => {resolve('OK');});
    });
    let p2 = Promise.reject('Success');
    let p3 = Promise.resolve('Oh Yeah');
    //调用 race 方法
    let result = Promise.race([p1, p2, p3]);
    console.log(result);

promise.js -------------------------------------------------------------------------
//添加 race 方法
Promise.race = function (promises) {
  return new Promise((resolve, reject) => {
    for (let i = 0; i < promises.length; i++) {
      promises[i].then(v => {
        //修改返回对象的状态为 『成功』
        resolve(v);
      }, r => {
        //修改返回对象的状态为 『失败』
        reject(r);
      })
    }
  });
}

2.3 其他优化

2.3.1 回调函数『异步执行』

下面代码结果的正确顺序是:111 --> 333 --> 444

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

p1.then(value => {
    console.log(222);
}, reason => {
    console.log(444);
});

console.log(333);

而当前我们封装的 Promise 输出的结果为:111 --> 444 --> 333,这时需要将我们的 then 方法变成 异步方法 可以包裹一层定时器(不一定是定时器,开启异步即可)

原理:js 事件循环机制、宏任务与微任务

// resolve 函数
function resolve(data){
    ...
    setTimeout(() => {
        self.callbacks.forEach(item => {
            item.onResolved(data);
        });
	});
}
// reject 函数
function reject(data){
    ...
    setTimeout(() => {
        self.callbacks.forEach(item => {
            item.onRejected(data);
        });
    });
}

// 添加 then 方法
Promise.prototype.then = function(onResolved, onRejected){
    ...
    return new Promise((resolve, reject) => {
        ...
        if (this.PromiseState === 'fulfilled') {
            setTimeout(() => {
                callback(onResolved);
            });
        }
        if (this.PromiseState === 'rejected') {
            setTimeout(() => {
                callback(onResolved);
            });       
        }
        ...
    }
}

2.3.2 class 版本 Promise

class Promise {
    // 构造方法
    constructor(executor) {
        // 添加属性
        this.PromiseState = 'pending';
        this.PromiseResult = null;
        this.callbacks = [];

        // 保存实例对象的 this 指向
        const self = this;

        // resolve 函数
        function resolve(data) {
            // 判断状态
            if (self.PromiseState !== 'pending') return;
            // 1.修改对象的状态(PromiseState)
            self.PromiseState = 'fulfilled';
            // 2.设置对象结果值(PromiseResult)
            self.PromiseResult = data;
            // 调用成功的回调
            setTimeout(() => {
                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;
            // 调用失败的回调
            setTimeout(() => {
                self.callbacks.forEach(item => {
                    item.onRejected(data);
                });
            });
        }

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

    // then 方法封装
    then(onResolved, onRejected) {
        const self = this;

        // 判断回调函数参数
        // 不传 onRejected 参数就给个默认的
        if (typeof onRejected !== 'function') {
            onRejected = reason => {
                throw reason;
            };
        }
        // 不传 onResolved 参数也给个默认的
        if (typeof onResolved !== 'function') {
            onResolved = value => value;
        }

        return new Promise((resolve, reject) => {
            function callback(type) {
                try {
                    // 获取回调函数执行结果
                    let result = type(self.PromiseResult);
                    if (result instanceof Promise) {
                        // 如果是 Promise 对象,将该对象的状态和值传递给外层
                        result.then(v => {
                            resolve(v);
                        }, r => {
                            reject(r);
                        });
                    } else {
                        // 非Promise结果,状态为『成功』
                        resolve(result);
                    }
                } catch (e) {
                    reject(e);
                }
            }

            if (this.PromiseState === 'fulfilled') {
                setTimeout(() => {
                    callback(onResolved);
                });
            }
            if (this.PromiseState === 'rejected') {
                setTimeout(() => {
                    callback(onRejected);
                });
            }
            //判断 pending 状态
            if (this.PromiseState === 'pending') {
                //保存回调函数
                this.callbacks.push({
                    onResolved: function () {
                        callback(onResolved);
                    },
                    onRejected: function () {
                        callback(onRejected);
                    },
                });
            }
        });
    }

    // catch 方法
    catch(onRejected) {
        return this.then(undefined, onRejected);
    }

    // Promise.resolve
    static resolve(value) {
        // 返回 Promise 对象
        return new Promise((resolve, reject) => {
            // Promise 对象则向外层传递状态的值
            if (value instanceof Promise) {
                value.then(v => {
                    resolve(v);
                }, r => {
                    reject(r);
                });
            } else {
                // 非 Promise 对象,成功
                resolve(value);
            }
        });
    }

    // Promise.reject
    static reject(reason) {
        return new Promise((resolve, reject) => {
            reject(reason);
        });
    }

    // Promise.all
    static all(promises) {
        return new Promise((resolve, reject) => {
            // 声明变量
            let count = 0;
            let arr = [];
            // 遍历
            for (let i = 0; i < promises.length; i++) {
                promises[i].then(v => {
                    // 记录里面成功的promise对象数量
                    count++;
                    // 将当前 promise 对象成功的结果存到数组中
                    arr[i] = v;
                    if (count === promises.length) {
                        resolve(arr);
                    }
                }, r => {
                    reject(r);
                });
            }
        });
    }

    // Promise.race
    static race(promises) {
        return new Promise((resolve, reject) => {
            for (let i = 0; i < promises.length; i++) {
                promises[i].then(v => {
                    resolve(v);
                }, r => {
                    reject(r);
                });
            }
        });
    }
}

3. async + await

1️⃣ async函数

给异步行为套一层 async function 变为异步函数,并且始终返回一个 Promise 对象

  • 函数的返回值为 promise 对象

  • promise 对象的结果由 async 函数执行的返回值决定

2️⃣ await表达式

  1. await 可以理解为是 async wait 的简写。await 必须出现在 async 函数内部,不能单独使用。
  2. await 后面可以跟任何的JS 表达式。虽然说 await 可以等很多类型的东西,但是它最主要的意图是用来等待 Promise 对象的状态被 resolved。如果await的是 promise对象会造成异步函数停止执行并且等待 promise 的解决,如果等的是正常的表达式则立即执行
  • await 右侧的表达式一般为 promise 对象,但也可以是其它的值

  • 如果表达式是 promise 对象,await 返回的是 promise 成功的值;如果 await 的 promise 失败了,就会抛出异常,需要通过 try…catch 捕获处理

  • 如果表达式是其它值,直接将此值作为 await 的返回值

async + await

简化了异步操作,可以写的像同步一样,便于理解和阅读,有效地避免了回调地狱和 Promise 链的情况

读文件的异步操作:

const fs = require('fs');

fs.readFile('./resource/1.html', (err, data1) => {
    if (err) throw err;
    fs.readFile('./resource/2.html', (err, data2) => {
        if (err) throw err;
        fs.readFile('./resource/3.html', (err, data3) => {
            if (err) throw err;
            console.log(data1 + data2 + data3);
        });
    });
});

使用 async + await 简化:

const fs = require('fs');
const util = require('util');
const mineReadFile = util.promisify(fs.readFile);

async function main() {
    try {
        let data1 = await mineReadFile('./resource/1.html');
        let data2 = await mineReadFile('./resource/2.html');
        let data3 = await mineReadFile('./resource/3.html');
        console.log(data1 + data2 + data3);
    } catch (e) {
        console.log(e);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值