这里是我自定义的Promise,若是想看原版,能够跳过最后有符合PromiseA+规范的源码jquery
class 承诺 { constructor(处理器函数) { //1. 处理器函数是在_new 承诺(处理器函数)_的时候当作参数传入到构造函数里的,构造函数里会执行这个处理器函数. let self = this //2.注册函数执行时可能没有前缀,若是注册函数里用this可能就是window,这里把this赋给变量,造成闭包. this.状态 = '等待...' //3.这个属性有三个值,默认是等待,只有调用注册成功或注册失败才会改变这个属性的值,**只能改变一次** this.成功值 = undefined //4.这个成功值是由使用者调用注册成功函数的时候传进函数里的,而后咱们在注册成功函数给这个属性赋值,简单点说就是形参赋值给属性 this.失败缘由 = undefined //5.同上 //说明:这里须要了解'那时()'函数,能够看完'那时()'再来看,这两在异步的状况才会用到,使用者在调用'那时()'的时候,承诺仍然是等待状态(也就是说注册成功和注册失败两个函数没有被调用) this.成功回调函数组 = [] //6.承诺变为成功时要调用的函数,屡次调用then传函数,咱们就把'那时()'传进来的函数push到这个数组里, this.失败回调函数组 = [] //7.同上,承诺变为失败时要调用的函数 let 注册成功 = function (成功值) { //7.'注册成功()'函数当实参传进了处理器函数,这样使用者在编写处理器函数得时候能够根据状况调用. if (self.状态 === '等待...') { //8.这个判断的做用:承诺的状态只能由等待->成功或等待->失败,并且只能改变一次.咱们默认初始态是等待,若是不是等待说明以前已经调用过'注册成功或注册失败' self.成功值 = 成功值 //9.将使用者调用'注册成功(成功值)'函数传进来的值由对象属性保存,这个值在调用'那时()'传进来的函数时会用到 self.状态 = '成功' //10.调用注册成功,改变承诺状态为成功 for (let 函数 of self.成功回调函数组) { //11.调用'那时()'传进来的函数 函数(成功值) } // this.成功回调函数组.forEach(函数=>函数()) } } let 注册失败 = function (失败缘由) { //12. 同上 if (self.状态 === '等待...') { self.失败缘由 = 失败缘由 self.状态 = '失败' for (let 函数 of self.失败回调函数组) { 函数(失败缘由) } // this.成功回调函数组.forEach(函数=>函数()) } } try { 处理器函数(注册成功, 注册失败) //13.执行处理器函数,把咱们定义好的两个函数传进去,这样使用者就能够用这两个函数来改变承诺的状态和值 } catch (错误) { 注册失败(错误) //14.处理器函数是使用者编写的,有可能报错,出错了咱们就调用注册失败()来改变这个承诺的状态和值 } } 那时(成功的回调, 失败的回调) { //15.对象的'那时()'方法,由使用者传进来两个函数参数,规定当前承诺对象为成功时调用第一个,失败调用第二个 let self = this 成功的回调 = typeof 成功的回调 === 'function' ? 成功的回调 : 成功值 => 成功值 失败的回调 = typeof 失败的回调 === 'function' ? 失败的回调 : 失败缘由 => { throw 失败缘由 } //16.这两个参数是可选参数,当他们不是函数时咱们给他默认值 let 新的承诺 = new 承诺((注册成功, 注册失败) => { //17.返回一个新的承诺,这样就能够链式调用'那时()'了,这里要注意新承诺的状态和值由'那时()'传进来的函数执行状况决定 //18.判断承诺的状态,决定当即执行回调仍是将回调函数push到回调函数组里等使用者调用注册成功()在注册成功()里执行 if (self.状态 === '等待...') { //19.若是是等待,显然承诺的成功值或失败值仍是undefined,因此咱们把他push到回调函数组里,让他在注册成功()或注册失败()函数里调用 self.成功回调函数组.push(() => { //20.这个函数要异步,由于咱们会在里面用到新承诺,然而新承诺如今还没被赋值,要完全理解这里应该须要js执行机制知识,目前我尚未.... setTimeout(() => { try { let 回调的返回值 = 成功的回调(self.成功值) //21.调用使用者传进来的函数获得返回值,若是使用者没有写返回语句默认是返回undefined 善后处理(新的承诺, 回调的返回值, 注册成功, 注册失败) //22.根据返回值决定咱们这个新承诺的值和状态 这里传进去的是咱们新承诺的注册成功和注册失败函数,咱们在里面调用他们来改变咱们新承诺的状态 } catch (错误) { 注册失败(错误) //23.若是执行使用者的函数出错就把咱们新承诺的状态和值改变 } }) }) self.失败回调函数组.push(() => { //24.同上 setTimeout(() => { try { let 回调的返回值 = 失败的回调(self.失败缘由) 善后处理(新的承诺, 回调的返回值, 注册成功, 注册失败) } catch (错误) { 注册失败(错误) } }) }) } if (self.状态 === '成功') { //25.若是承诺的状态是成功的说明注册成功()函数已经被调用了,承诺的状态和值都被使用者改变了, //咱们能够取到对应的值来传进回调里,让使用者用. setTimeout(() => { try { //逻辑同20-23 let 回调的返回值 = 成功的回调(self.成功值) 善后处理(新的承诺, 回调的返回值, 注册成功, 注册失败) } catch (错误) { 注册失败(错误) } }) } if (self.状态 === '失败') { //同上 setTimeout(() => { try { let 回调的返回值 = 失败的回调(self.失败缘由) 善后处理(新的承诺, 回调的返回值, 注册成功, 注册失败) } catch (错误) { 注册失败(错误) } }) } }) return 新的承诺 } }
//26.这个是核心,涉及到递归,这里我写的和PromiseA+规范的实现不一样,他判断的是thenable,兼容性好,我只是为了理解Promise原理因此就简化了. function 善后处理(新的承诺, 回调的返回值, 注册成功, 注册失败) { // let p2 = p.那时((成功值)=>{ // return p2 // }) if (回调的返回值 instanceof 承诺) { //27.判断使用者写的回调函数的返回值是否是一个承诺,若是不是承诺就直接调用咱们新承诺的注册成功()函数改变咱们新承诺的状态和值 //若是返回值是一个承诺咱们就获得这个承诺的值,把这个值给咱们新承诺的注册函数 if (新的承诺 === 回调的返回值) { //28.这里是为了解决这种使用状况 // let p2 = p.那时((成功值)=>{ // return p2 // }) 注册失败(new TypeError('循环引用')) return } try { 回调的返回值.那时((成功值) => { 善后处理(新的承诺, 成功值, 注册成功, 注册失败) //29.若是使用者返回的承诺的值仍是一个承诺,继续'那时()'直到不是承诺 //注意:这里传进去的注册成功,注册失败是咱们新承诺的注册函数,递归进去,当不是承诺时就改变咱们新承诺的状态和值了,而后递归一层层返回 //这里是进递归,其余状况就是出递归 }, (失败缘由) => { 注册失败(失败缘由) }) } catch (错误) { 注册失败(错误) } } else { 注册成功(回调的返回值) } }
//测试一 p = new 承诺((注册成功, 注册失败) => { setTimeout(()=>{ 注册失败('abc') },1000) }) p.那时((成功的值) => { console.log(成功的值) },(失败缘由)=>{ console.log(失败缘由) }) //输出abc
//测试二:返回值是承诺 p = new 承诺((注册成功, 注册失败) => { setTimeout(() => { 注册成功('我是最外面的') }, 1000) }) p.那时((成功值) => { console.log(成功值) let p11 = new 承诺((注册成功, 注册失败) => { let p22 = new 承诺((注册成功, 注册失败) => { 注册成功('最里层') }) 注册成功(p22) }) return p11 }, 1) .那时((成功值) => { console.log(成功值) }, (失败缘由) => { console.log(失败缘由) }) //输出: //我是最外层 //我是最里层
//测试三:失败穿透 p = new 承诺((注册成功, 注册失败) => { throw '(╥╯^╰╥)' setTimeout(() => { 注册成功('♪(´▽`)') }, 1000) }) p.那时((成功的值) => { console.log(成功的值) },(失败缘由)=>{ console.log('第一次失败:'+失败缘由) throw '(╥╯^╰╥))' }).那时( (成功的值) => { console.log(成功的值) } ).那时(null,(失败缘由) => { console.log('失败穿透'+失败缘由) }) //输出 //第一次失败:(╥╯^╰╥) //失败穿透(╥╯^╰╥))
function Promise(executor) { let self = this; self.value = undefined; // 成功的值 self.reason = undefined; // 失败的值 self.status = 'pending'; // 目前promise的状态pending self.onResolvedCallbacks = []; // 可能new Promise的时候会存在异步操做,把成功和失败的回调保存起来 self.onRejectedCallbacks = []; function resolve(value) { // 把状态更改成成功 if (self.status === 'pending') { // 只有在pending的状态才能转为成功态 self.value = value; self.status = 'resolved'; self.onResolvedCallbacks.forEach(fn => fn()); // 把new Promise时异步操做,存在的成功回调保存起来 } } function reject(reason) { // 把状态更改成失败 if (self.status === 'pending') { // 只有在pending的状态才能转为失败态 self.reason = reason; self.status = 'rejected'; self.onRejectedCallbacks.forEach(fn => fn()); // 把new Promise时异步操做,存在的失败回调保存起来 } } try { // 在new Promise的时候,当即执行的函数,称为执行器 executor(resolve, reject); } catch (e) { // 若是执行executor抛出错误,则会走失败reject reject(e); } } // then调用的时候,都是属于异步,是一个微任务 // 微任务会比宏任务先执行 // onFulfilled为成功的回调,onRejected为失败的回调 Promise.prototype.then = function (onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val; onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err } let self = this; let promise2; // 上面讲了,promise和jquery的区别,promise不能单纯返回自身, // 而是每次都是返回一个新的promise,才能够实现链式调用, // 由于同一个promise的pending resolve reject只能更改一次 promise2 = new Promise((resolve, reject) => { if (this.status === 'resolved') { // 为何要加setTimeout? // 首先是promiseA+规范要求的 // 其次是你们写的代码,有的是同步,有的是异步 // 因此为了更加统一,就使用为setTimeout变为异步了,保持一致性 setTimeout(() => { try { // 上面executor虽然使用try catch捕捉错误 // 可是在异步中,不必定可以捕捉,因此在这里 // 用try catch捕捉 let x = onFulfilled(self.value); // 在then中,返回值多是一个promise,因此 // 须要resolvePromise对返回值进行判断 resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0) } if (self.status === 'rejected') { setTimeout(() => { try { let x = onRejected(self.reason); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0) } if (self.status === 'pending') { self.onResolvedCallbacks.push(() => { setTimeout(() => { try { let x = onFulfilled(self.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0) }); self.onRejectedCallbacks.push(() => { setTimeout(() => { try { let x = onRejected(self.reason); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0) }); } }); return promise2 } function resolvePromise(promise2, x, resolve, reject) { // 3.从2中咱们能够得出,本身不能等于本身 // 当promise2和x是同一个对象的时候,则走reject if (promise2 === x) { return reject(new TypeError('Chaining cycle detected for promise')) } // 4.由于then中的返回值能够为promise,当x为对象或者函数,才有可能返回的是promise let called if (x !== null && (typeof x === 'object' || typeof x === 'function')) { // 8.从第7步,能够看出为何会存在抛出异常的可能,因此使用try catch处理 try { // 6.由于当x为promise的话,是存在then方法的 // 可是咱们取一个对象上的属性,也有可能出现异常,咱们能够看一下第7步 let then = x.then // 9.咱们为何在这里用call呢?解决了什么问题呢?能够看上面的第10步 // x可能仍是个promise,那么就让这个promise执行 // 可是仍是存在一个恶做剧的状况,就是{then:{}} // 此时须要新增一个判断then是否函数 if (typeof then === 'function') { then.call(x, (y) => { // y是返回promise后的成功结果 // 一开始咱们在这里写的是resolve(y),可是考虑到一点 // 这个y,有可能仍是一个promise, // 也就是说resolve(new Promise(...)) // 因此涉及到递归,咱们把resolve(y)改为如下 // 12.限制既调resolve,也调reject if (called) return called = true resolvePromise(promise2, y, resolve, reject) // 这样的话,代码会一直递归,取到最后一层promise // 11.这里有一种状况,就是不能既调成功也调失败,只能挑一次, // 可是咱们前面不是处理过这个状况了吗? // 理论上是这样的,可是咱们前面也说了,resolvePromise这个函数 // 是全部promise通用的,也能够是别人写的promise,若是别人 // 的promise可能既会调resolve也会调reject,那么就会出问题了,因此咱们接下来要 // 作一下限制,这个咱们写在第12步 }, (err) => { // err是返回promise后的失败结果 if (called) return called = true reject(err) }) } else { if (called) return; called = true; resolve(x) // 若是then不是函数的话,那么则是普通对象,直接走resolve成功 } } catch (e) { // 当出现异常则直接走reject失败 if (called) return called = true reject(e) } } else { // 5.x为一个常量,则是走resolve成功 resolve(x) } } module.exports = Promise; Promise.defer = Promise.deferred = function () { let dfd = {}; dfd.promise = new Promise((resolve, reject) => { dfd.resolve = resolve; dfd.reject = reject; }); return dfd; } //执行命令promises-aplus-tests promise.js检测是否符合promiseA+规范,先安装包