前端开发中常常会用到Promise,不过有局部人并不分明Promise的原理,本文也是自己在学习Promise时对Promis的一些意识,心愿能对各位童鞋有所帮忙。
手写Promise - 实现一个根底的Promise
[手写Promise - 实例办法catch、finally]()
[手写Promise - 罕用静态方法all、any、resolve、reject、race]()
从意识Promise开始。。。
/* 模仿一个简略的异步行为 */function fetchData() { return new Promise((resolve, reject) => { setTimeout(() => { resolve('willem'); }, 1000); });}fetchData().then((data) => { // after 1000ms console.log(data); // willem return 'wei';}, (err) => {}).then((data2) => { console.log(data2); // wei});
下面的例子算是一个最常见的用法,可能在应用的时候更多的应用的是catch
来解决异样来代替then
办法的第二个参数,但catch
也只是一个then
的语法糖。
从中咱们能够用一些句子来形容Promise。
- promise是一个类,它的构造函数承受一个函数,函数的两个参数也都是函数
- 在传入的函数中执行resolve示意胜利,执行reject示意失败,传入的值会传给then办法的回调函数
- promise有一个叫做then的办法,该办法有两个参数,第一个参数是胜利之后执行的回调函数,第二个参数是失败之后执行的回调函数。then办法在resolve或者reject执行之后才会执行,并且then办法中的值是传给resolve或reject的参数
- promise反对链式调用
有了相应的形容,接下来就是来一步一步实现了。
简略版Promise
1. promise是一个类,它的构造函数承受一个函数,函数的两个参数也都是函数
第一点比较简单
// 这里没有应用Promise作为类名是为了不便测试class WPromise { constructor(executor) { // 这里绑定this是为了避免执行时this的指向扭转,this的指向问题,这里不过多赘述 executor(this._resolve.bind(this), this._reject.bind(this)); } _resolve() {} _reject() {}}
2. 在传入的函数中执行resolve示意胜利,执行reject示意失败,传入的值会传给then办法的回调函数
胜利、失败,这个很容易想到应用一个状态进行标记,实际上Promise就是这样做的。在Promise中应用了pending、fulfilled、rejected
来标识以后的状态。
pending
初始状态,既不是胜利,也不是失败状态。期待resolve或者reject调用更新状态。fulfilled
意味着操作胜利实现。rejected
意味着操作失败。
须要留神的一点是,这三个状态之间只存在两个变换关系:
pending
转换为fulfilled
,只能由resolve办法实现转换pending
转换为rejected
,只能由reject办法实现转换
传入的值会传给then
的回调函数,怎么传递呢?显然咱们将对resolve和reject的值做一个保留。
将下面的状态和值增加到Promise
class WPromise { static pending = 'pending'; static fulfilled = 'fulfilled'; static rejected = 'rejected'; constructor(executor) { executor(this._resolve.bind(this), this._reject.bind(this)); this.status = WPromise.pending; // 初始化状态为pending this.value = undefined; // 存储 this._resolve 即操作胜利 返回的值 this.reason = undefined; // 存储 this._reject 即操作失败 返回的值 } _resolve(value) { this.value = value; this.status = WPromise.fulfilled; // 将状态设置为胜利 } _reject(reason) { this.reason = reason; this.status = WPromise.rejected; // 将状态设置为失败 }}
3. Promise有一个叫做then的办法,该办法有两个参数,第一个参数是胜利之后执行的回调函数,第二个参数是失败之后执行的回调函数。then办法在resolve或者reject执行之后才会执行,并且then办法中的值是传给resolve或reject的参数
这句话有点长,须要留神的是这句then办法在resolve或者reject执行之后才会执行
,咱们晓得Promise是异步的,也就是说then
传入的函数是不能立马执行,须要存储起来,在resolve函数执行之后才拿进去执行。
换句话说,这个过程有点相似于公布订阅者模式
:咱们应用then来注册事件,那什么时候来告诉这些事件是否执行呢?答案就是在resolve办法执行或者reject办法执行时。
ok, 持续欠缺咱们的代码。
class WPromise { static pending = "pending"; static fulfilled = "fulfilled"; static rejected = "rejected"; constructor(executor) { executor(this._resolve.bind(this), this._reject.bind(this)); this.status = WPromise.pending; // 初始化状态为pending this.value = undefined; // 存储 this._resolve 即操作胜利 返回的值 this.reason = undefined; // 存储 this._reject 即操作失败 返回的值 // 存储then中传入的参数 // 至于为什么是数组呢?因为同一个Promise的then办法能够调用屡次 this.callbacks = []; } // onFulfilled 是胜利时执行的函数 // onRejected 是失败时执行的函数 then(onFulfilled, onRejected) { // 这里能够了解为在注册事件 // 也就是将须要执行的回调函数存储起来 this.callbacks.push({ onFulfilled, onRejected, }); } _resolve(value) { this.value = value; this.status = WPromise.fulfilled; // 将状态设置为胜利 // 告诉事件执行 this.callbacks.forEach((cb) => this._handler(cb)); } _reject(reason) { this.reason = reason; this.status = WPromise.rejected; // 将状态设置为失败 this.callbacks.forEach((cb) => this._handler(cb)); } _handler(callback) { const { onFulfilled, onRejected } = callback; if (this.status === WPromise.fulfilled && onFulfilled) { // 传入存储的值 onFulfilled(this.value); } if (this.status === WPromise.rejected && onRejected) { // 传入存储的错误信息 onRejected(this.reason); } }}
这个时候的Promise曾经渐具雏形,当初能够来简略测试一下
function fetchData(success) { return new WPromise((resolve, reject) => { setTimeout(() => { if (success) { resolve("willem"); } else { reject('error'); } }, 1000); });}fetchData(true).then(data => { console.log(data); // after 1000ms: willem});fetchData(false).then(null, (reason) => { console.log(reason); // after 1000ms: error});
从下面的输入后果来看,临时是没什么问题的。接下来就是须要重点关注的链式调用问题了。
重难点:链式调用
链式调用
不晓得你们看见这个想到了啥,我反正是想到了jQuery。其实链式调用无非就是再返回一个类的实例,那首先想到的必定就是间接返回this
,不过反正本身真的能够吗?
咱们无妨在then办法最初增加一行 return this;
来进行一个测试
function fetchData() { return new WPromise((resolve, reject) => { setTimeout(() => { resolve('willem'); }, 1000); });}const p1 = fetchData().then(data1 => {return data1 + ' wei'});const p2 = p1.then((data2) => {console.log(data2);}); // willem 正确输入应该是 'willem wei'const p3 = p1.then((data3) => {console.log(data3);}); // willem 正确输入应该是 'willem wei'
显然,间接返回this是必定不对,必定要对函数的返回值做一个解决。
这时候可能会有同学说了,那我解决不就完事了么,我把then回调函数的执行后果赋值给value不就完事。答案当然是否定的,这回引发Promise外部的value和callbacks凌乱。
那么,咱们采取的当然是另一个计划,每次then办法都将返回一个新的Promise
这是一个简略的then的数据走向。简略说一下,then函数中返回的Promise的value值来源于以后then函数的onFulfilled函数(第一个参数)的执行后果(为不便了解,临时只探讨操作胜利的状况
)。
从咱们写的代码来看,value值只会在resolve函数中被赋值,显然咱们也将会把onFulfilled执行的后果通过resolve的执行来传入到下一个Promise中。
退出链式调用的解决:
class WPromise { static pending = "pending"; static fulfilled = "fulfilled"; static rejected = "rejected"; constructor(executor) { executor(this._resolve.bind(this), this._reject.bind(this)); this.status = WPromise.pending; // 初始化状态为pending this.value = undefined; // 存储 this._resolve 即操作胜利 返回的值 this.reason = undefined; // 存储 this._reject 即操作失败 返回的值 // 存储then中传入的参数 // 至于为什么是数组呢?因为同一个Promise的then办法能够调用屡次 this.callbacks = []; } // onFulfilled 是胜利时执行的函数 // onRejected 是失败时执行的函数 then(onFulfilled, onRejected) { // 返回一个新的Promise return new WPromise((nextResolve, nextReject) => { // 这里之所以把下一个Promsie的resolve函数和reject函数也存在callback中 // 是为了将onFulfilled的执行后果通过nextResolve传入到下一个Promise作为它的value值 this.callbacks.push({ nextResolve, nextReject, onFulfilled, onRejected }); }); } _resolve(value) { this.value = value; this.status = WPromise.fulfilled; // 将状态设置为胜利 // 告诉事件执行 this.callbacks.forEach((cb) => this._handler(cb)); } _reject(reason) { this.reason = reason; this.status = WPromise.rejected; // 将状态设置为失败 this.callbacks.forEach((cb) => this._handler(cb)); } _handler(callback) { const { onFulfilled, onRejected, nextResolve, nextReject } = callback; if (this.status === WPromise.fulfilled) { // 传入存储的值 // 未传入onFulfilled时,将undefined传入 const nextValue = onFulfilled ? onFulfilled(this.value) : undefined; nextResolve(nextValue); return; } if (this.status === WPromise.rejected) { // 传入存储的错误信息 // 同样的解决 const nextReason = onRejected ? onRejected(this.value) : undefined; nextReject(nextReason); } }}
咱们再把刚开始的例子拿来测试一下
function fetchData() { return new WPromise((resolve, reject) => { setTimeout(() => { resolve('willem'); }, 1000); });}fetchData().then((data) => { // after 1000ms console.log(data); // willem return 'wei';}, (err) => {}).then((data2) => { console.log(data2); // wei});
哟西,没啥问题。不过下面的版本还有个问题没有解决,当onFulfilled执行的后果不是一个简略的值,而就是一个Promise时,后续的then会期待其执行实现之后才执行。
Promise根底版的最终版:
class WPromise { static pending = "pending"; static fulfilled = "fulfilled"; static rejected = "rejected"; constructor(executor) { executor(this._resolve.bind(this), this._reject.bind(this)); this.status = WPromise.pending; // 初始化状态为pending this.value = undefined; // 存储 this._resolve 即操作胜利 返回的值 this.reason = undefined; // 存储 this._reject 即操作失败 返回的值 // 存储then中传入的参数 // 至于为什么是数组呢?因为同一个Promise的then办法能够调用屡次 this.callbacks = []; } // onFulfilled 是胜利时执行的函数 // onRejected 是失败时执行的函数 then(onFulfilled, onRejected) { // 返回一个新的Promise return new WPromise((nextResolve, nextReject) => { // 这里之所以把下一个Promsie的resolve函数和reject函数也存在callback中 // 是为了将onFulfilled的执行后果通过nextResolve传入到下一个Promise作为它的value值 this.callbacks.push({ nextResolve, nextReject, onFulfilled, onRejected }); }); } _resolve(value) { // 解决onFulfilled执行后果是一个Promise时的状况 // 这里可能了解起来有点艰难 // 当value instanof WPromise时,阐明以后Promise必定不会是第一个Promise // 而是后续then办法返回的Promise(第二个Promise) // 咱们要获取的是value中的value值(有点绕,value是个promise时,那么外部存有个value的变量) // 怎么将value的value值获取到呢,能够将传递一个函数作为value.then的onFulfilled参数 // 那么在value的外部则会执行这个函数,咱们只须要将以后Promise的value值赋值为value的value即可 if (value instanceof WPromise) { value.then(this._resolve.bind(this), this._reject.bind(this)); return; } this.value = value; this.status = WPromise.fulfilled; // 将状态设置为胜利 // 告诉事件执行 this.callbacks.forEach((cb) => this._handler(cb)); } _reject(reason) { this.reason = reason; this.status = WPromise.rejected; // 将状态设置为失败 this.callbacks.forEach((cb) => this._handler(cb)); } _handler(callback) { const { onFulfilled, onRejected, nextResolve, nextReject } = callback; if (this.status === WPromise.fulfilled) { // 传入存储的值 // 未传入onFulfilled时,将undefined传入 const nextValue = onFulfilled ? onFulfilled(this.value) : undefined; nextResolve(nextValue); return; } if (this.status === WPromise.rejected) { // 传入存储的错误信息 // 同样的解决 const nextReason = onRejected ? onRejected(this.value) : undefined; nextReject(nextReason); } }}
ok,测试一下
function fetchData() { return new WPromise((resolve, reject) => { setTimeout(() => { resolve('willem'); }, 1000); });}fetchData().then((data) => { return new WPromise(resolve => { setTimeout(() => { resolve(data + ' wei'); }, 1000); });}, (err) => {}).then((data2) => { console.log(data2); // willem wei});
至此,一个简略的Promise就实现了,当然还有很多须要解决,比方异样等等。
下一篇文章咱们一起再来学习一下finally和catch
的实现。
更多:上述模仿Promise的残缺代码