在手写Promise之前,我们需要了解一下什么是Promise,为什么要引入Promise。
1.Promise是ES6引入的一种解决异步编程的方案。
2.为什么要引入Promise呢?可以从问题的根源出手,在我们写一些嵌套的回调函数时会发现,最基本的问题就是回调地狱的产生,同时伴随着当我们不去手动处理回调结果和异常时,我们就很头疼,没办法直接捕获到结果和异常,这时候,Promise就诞生了,他就相当于一个助手,当你将你所需要的回调让他去处理时,他会根据你说提供的回调返回给你一个“正确”、“错误”或者“异常”的结果,同样,也能有效的解决回调地狱的现象(当然是需要配合关键字使用)
到这,你可能还是不明白Promise到底是什么,那么还是从需求入手吧
需求: 第一秒内读取第一个文件,第二秒内读取第二个文件,第三秒内读取第三个文件。
传统的写法如下:
const fs = require('fs')
setTimeout(() => {
fs.readFile("./文件一.html", (err, data1) => {
console.log(data1.toString())
})
setTimeout(() => {
fs.readFile("./文件二.html", (err, data2) => {
console.log(data2)
})
setTimeout(() => {
fs.readFile("./文件三.html", (err, data3) => {
console.log(data3)
})
}, 3000);
}, 2000);
}, 1000)
很明显,当所读取的文件增加到几百个甚至上千个的时候,代码右边的长度就会无线延伸下去
然后,看看Promise的效果
// 创建一个Promise
let p = new Promise((resolve, reject) => {
resolve("Ok")
})
p.then(value => {
setTimeout(() => {
fs.readFile("文件一.html", (err, data) => {
console.log(data)
})
}, 2000);
}, reason => {
}).then(value => {
setTimeout(() => {
fs.readFile("文件二.html", (err, data) => {
console.log(data)
})
}, 3000);
}, reason => {
}).then(value => {
setTimeout(() => {
fs.readFile("文件三.html", (err, data) => {
console.log(data)
})
}, 1000);
}, reason => {
})
显然,回调地狱的问题就解决了,当然,这还不是Promise的全貌,只是Promise许多用法的其中一种,这里也就不一一举出了
回归主题,手写Promise,当然实际开发中,并不会自己手写这个库,但是手写能够很大程度上的帮助我们理解Promise的基本原理和用法。
// 下面针对有一定Promise基础的学者
1.首先,Promise对象中,有最基本的两个属性,PromiseState(状态属性)、PromiseResult(结果属性,默认值为null),状态属性有三种情况“pending”、“fulfilled / resolved”、“rejected”,分别是基础状态(Promise创建之初的状态)、成功状态(调用resolve()函数之后改变成的状态,并执行成功回调)、失败状态(调用reject()函数之后改变成的状态,并执行失败回调 / 或者抛出异常之后调用reject()函数(本质是调用函数改变状态)),通过这些内容,我们就可以构建最基本的Promise的雏形:
function Promise(excutor) {
// 状态属性 、 结果属性
this.PromiseState = "pending"
this.PromiseResult = null
// 内部 resolve函数
function resolve(data) {
}
// 内部 reject函数
function reject(data) {
}
}
接下来就要处理resolve、reject这两个函数了,调用这两个函数时需要改变Promise的状态和Promise的结果,并且,值得注意的是,Promise的状态一旦改变状态,就不能再次调用这两个函数改变状态,则,得到以下的Promise:
function Promise(excutor) {
// 状态属性 、 结果属性
this.PromiseState = "pending"
this.PromiseResult = null
// 保存实例对象的 this
const _this = this
// 内部 resolve函数
function resolve(data) {
// 判断状态是否已经改变
if (_this.PromiseState !== "pending") return
_this.PromiseState = "fulfilled"
_this.PromiseResult = data
}
// 内部 reject函数
function reject(data) {
// 判断状态是否已经改变
if (_this.PromiseState !== "pending") return
_this.PromiseState = "rejected"
_this.PromiseResult = data
}
}
这时候我们需要想到一种情况,那就是前面提到的抛出异常的情况,而什么时候会抛出异常呢,显然,在程序执行的过程中,随时会抛出异常,也就是说,在Promise对象中的任何地方都有可能抛出异常,那么我们就需要,通过try...catch去捕获异常,最简单的方法就是将这Promise整个包裹起来去捕获,这样显得很累赘,太杂糅,我们可以同步的调用执行器函数,也就是“备份”,就是的再另一个地方同步重新跑一遍,以便捕获异常,这也就得到了最基本的Promise:
function Promise(excutor) {
// 状态属性 、 结果属性
this.PromiseState = "pending"
this.PromiseResult = null
// 保存实例对象的 this
const _this = this
// 内部 resolve函数
function resolve(data) {
// 判断状态是否已经改变
if (_this.PromiseState !== "pending") return
_this.PromiseState = "fulfilled"
_this.PromiseResult = data
}
// 内部 reject函数
function reject(data) {
// 判断状态是否已经改变
if (_this.PromiseState !== "pending") return
_this.PromiseState = "rejected"
_this.PromiseResult = data
}
// 同步捕获异常
try {
// 同步调用 【执行器函数】
excutor(resolve, reject)
} catch (e) {
reject(e)
}
}
接下来会有相关文章持续更新之后的Promise的优化和相关的 then() 、all() 等函数的封装