Open libin1991 opened 6 years ago
该实现是按照promiseA+规范来进行梳理的 当使用promise的时候需要先new一个实例,所以我们要构造一个promise构造函数
let p = new Promise(function(resolve, reject){ // 先调谁,就走谁 reject('不对了'); // 失败 resolve(1); // 成功 // 如果抛出异常也会走到then的失败函数里 throw new Error('错了'); }); p.then(function(data) { console.log(data); }, function(err) { console.log(err); // '不对了' });
new的Promise实例包含一个执行函数(executor),该函数有两个参数,分别是成功和失败的方法
这里要记住一点是成功和失败的方法是互斥的,同时调用时,先调用的先执行,后面调用的不会再执行
都知道promise是有三种状态值的
那么给构造函数写一个status,用来保存状态的变化 pendding可以转为fulfilled或者rejected, 一旦转换了,status就不会改变了,要么是成功态,要么是失败态
// 实现promise function Promise(executor) { const self = this; self.status = 'pending'; // 默认的状态,pending->fulfilled, pending->rejected self.value = null; // 成功的值 self.reason = null; // 失败的原因 function resolve(value) { // 成功 if (self.status === 'pending') { self.status = 'fulfilled'; self.value = value; } } function reject(reason) { // 失败 if (self.status === 'pending') { self.status = 'rejected'; self.reason = reason; } } try { executor(resolve, reject); } catch(e) { // 如果抛出错误,就直接走失败的方法 reject(e); } } Promise.prototype.then = function(onFulfilled, onRejected) { const self = this; if (self.status === 'fulfilled') { onFulfilled(); } if (self.status === 'rejected') { onRejected(); } }; module.exports = Promise;
上面写了很多,但是发现都是处理同步的情况,当遇到异步的时候该怎么处理呢,我们继续往下写
promise中可以写异步代码,并且可以进行多次then
let p = new Promise(function(resolve, reject){ // 异步操作 setTimeout(function() { resolve('ok'); }, 1000); }); p.then(function(data) { console.log(data); }, function(err) { console.log(err); }); p.then(function(data) { console.log(data); }, function(err) { console.log(err); });
promise实例可以多次then,当成功后会将then中的成功的方法依次执行, 我们可以先将then中成功的回调和失败的回调存到数组内,当成功时调用成功的数组即可
function Promise() { self.status = 'pending'; self.value = null; self.reason = null; + self.onFulfilledCb = []; // 存放then成功的回调 + self.onRejectedCb = []; // 存放then失败的回调 function resolve(value) { if (self.status === 'pending') { self.status = 'fulfilled'; self.value = value; + self.onFulfilledCb.forEach(function (fn) { fn(); }); } } function reject(reason) { if (self.status === 'pending') { self.status = 'rejected'; self.reason = reason; + self.onRejectedCb.forEach(function (fn) { fn(); }); } } try { executor(resolve, reject); } catch(e) { // 如果抛出错误,就直接走失败的方法 reject(e); } } Promise.prototype.then = function(onFulfilled, onRejected) { const self = this; if (self.status === 'fulfilled') { onFulfilled(self.value); } if (self.status === 'rejected') { onRejected(self.reason); } // 当调用then时 可能没成功也没失败,就处于pending状态 + if (self.status === 'pending') { // 将成功的回调添加到数组中 self.onFulfilledCb.push(function () { onFulfilled(self.value); }); self.onRejectedCb.push(function () { onRejected(self.reason); }); } }; module.exports = Promise;
上面的代码实现了异步调用的时候then方法是怎么处理的情况,
那么接下来就来到重要的环节了,大家都知道promise的then方法也是支持链式调用的 不过then的链式调用可不同于jquery返回this的方式实现的
⭐️ 它是返回了一个新的promise
下面来看代码
let p = new Promise(function(resolve, reject) { resolve('ok'); }); p.then(function (data) { console.log(data); return data; }, function (err) { console.log(err); // return '有返回值就走成功态' }).then(function (data) { console.log('2 '+data); }, function (err) { console.log(err); })
now让我们开始实现链式调用,还是老样子,+号代表实现的主要代码:
Promise.prototype.then = function (onFulfilled, onRejected) { const self = this; + let promise2; // 定义一个promise2变量 if (self.status === 'fulfilled') { // 返回一个新的promise + promise2 = new Promise(function (resolve, reject) { // x可能是个普通值,也可能是个promise + let x = onFulfilled(self.value); // x也可能是别人写的promise,写一个函数统一去处理 + resolvePromise(promise2, x, resolve, reject); }); } if (self.status === 'rejected') { + promise2 = new Promise(function (resolve, reject) { + let x = onRejected(self.reason); + resolvePromise(promise2, x, resolve, reject); }); } // 当调用then时 可能没成功也没失败,就处于pending状态 if (self.status === 'pending') { // 将成功的回调添加到数组中 + promise2 = new Promise(function (resolve, reject) { self.onFulfilledCb.push(function () { + let x = onFulfilled(self.value); + resolvePromise(promise2, x, resolve, reject); }); self.onRejectedCb.push(function () { + let x = onRejected(self.reason); + resolvePromise(promise2, x, resolve, reject); }); }); } return promise2; }; function resolvePromise(p2, x, resolve, reject) { if (p2 === x) { // 不能返回自己 return reject(new TypeError('循环引用')); } let called; // 表示是否调用成功or失败 // x返回的可能是对象和函数也可能是一个普通的值 if (x !== null && (typeof x === 'object' || typeof x === 'function')) { try { let then = x.then; if (typeof then === 'function') { then.call(x, function (y) { // 防止多次调用 if (called) return; called = true; // y可能还是个promise,所以递归继续解析只到返回一个普通值 resolvePromise(p2, y, resolve, reject); }, function (e) { if (called) return; called = true; reject(e); }); } } catch(e) { if (called) return; called = true; reject(e); } } else { resolve(x); // 返回一个普通值 } }
通过以上代码也可以得出了一些结论
以上我们已经实现了promise的链式调用了,但这还不够promise有一种情况是在then中什么都不传的情况,还继续链式调用
let p = new Promise(function (resolve, reject) { reject(999); }); p.then().then().then(function (data) { console.log(data); // 999 }, function (err) { console.log(err); });
这就是promise中值的穿透,该实现是在then中,那么我们也对then方法加以改良去实现一下
Promise.prototype.then = function(onFulfilled, onRejected) { + onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) { return value }; + onRejected = typeof onRejected === 'function' ? onFulfilled : function (err) { throw err }; const self = this; let promise2; };
其实很简单,就是在then的时候如果你不给我传回调函数,那么我就判断一下,确实没有传,那我就给你新创建一个函数然后在成功回调的时候把值传到下一个then中去
另外在promise规范中要求,所有的onFulfilled和onRejected都需要异步执行,所以我们要给他们加上异步,显然这个更容易实现了,看下面代码
Promise.prototype.then = function(onFulfilled, onRejected) { if (self.status === 'fulfilled') { promise2 = new Promise(function (resolve, reject) { // 当成功或失败执行时,有异常那么返回的promise应该处于失败态 // 用try/catch来防止返回异常的情况 // 加上setTimeout实现异步调用 + setTimeout(function () { try { let x = onFulfilled(self.value); resolvePromise(promise2, x, resolve, reject); } catch(e) { reject(e); } }); }); } if (self.status === 'rejected') { promise2 = new Promise(function (resolve, reject) { + setTimeout(function () { try { let x = onRejected(self.reason); resolvePromise(promise2, x, resolve, reject); } catch(e) { reject(e); } }); }); } }; if (self.status === 'pending') { // 将成功的回调添加到数组中 promise2 = new Promise(function (resolve, reject) { self.onFulfilledCb.push(function () { + setTimeout(function () { try { let x = onFulfilled(self.value); resolvePromise(promise2, x, resolve, reject); } catch(e) { reject(e); } }); }); self.onRejectedCb.push(function () { + setTimeout(function () { try { let x = onRejected(self.reason); resolvePromise(promise2, x, resolve, reject); } catch(e) { reject(e); } }); }); } return promise2;
好了,我们以上就实现了promise中最重要的then方法了,写的不好请多理解吧。
不过既然把then都写完了,那接下来再写几个其他的,写多点也让大家一起来研究研究。
先来看下用法,其实这个和then方法里失败的回调onRejected是一样的
let p = new Promise(function(resolve, reject) { reject('错错错'); }); p.then(function(data){ console.log(data); }).catch(function(e){ console.log(e); // '错错错' });
下面上代码:
// 捕获错误的方法 Promise.prototype.catch = function(callback) { // 也是调用then方法,给成功的回调传一个null,给失败的回调传入callback return this.then(null, callback); };
Promise类自身也有一些方法,常用的有all, race, resolve, reject
都写到这里了,那就继续写下去,废话不多说,开撸!!!
Promise.all()的用法
let p = new Promise(function(resolve, reject) { setTimeout(function() { resolve('北京'); }, 1000); }); let p2 = new Promise(function(resolve, reject) { setTimeout(function() { resolve('南京'); }, 200); }); let p3 = new Promise(function(resolve, reject) { resolve('东京'); }); Promise.all([p, p2, p3]).then(function(data) { // all方法是所有的promise都成功后才会成功 // 按照传入all里的顺序依次执行,p里面的定时器只是执行完成的时间而已,不影响all里输出顺序 // 如:p这个需要等1秒后才会返回成功态,p2就需要等待1秒时间 console.log(data); // [ '北京', '南京', '东京' ] });
上面知道了用法,下面就来写实现,客官且留步继续看下去
Promise.all = function(items) { return new Promise(function(resolve, reject) { let res = []; // 用来存储成功函数返回的值 let num = 0; // 记录都返回成功的数字 let len = items.length; // 数组的长度 // 对数组进行遍历 for (let i = 0; i < len; i++) { items[i].then(function(data) { res[i] = data; if (++num === len) { resolve(res); } }, reject); } }); };
如此这般,这般如此,就实现了all
再接再厉还有race方法
那么race方法顾名思义,race就是比赛,就是在比谁快,谁先成功,返回的就是谁的成功数据
来看一下怎么用
let p = new Promise(function (resolve, reject) { setTimeout(function () { resolve('北京'); }, 1000); }); let p2 = new Promise(function (resolve, reject) { setTimeout(function () { resolve('南京'); }, 500); }); let p3 = new Promise(function (resolve, reject) { setTimeout(function () { resolve('东京'); }); }); Promise.race([p, p2, p3]).then(function (data) { // p3最先执行完返回成功,所以data就为p3的值,其他的p,p2都不返回 console.log(data); // '东京' });
那么,不啰嗦了,写吧
Promise.race = function(items) { return new Promise(function(resolve, reject) { for (let i = 0; i < items.length; i++) { items[i].then(resolve, reject); } }); };
以上就实现了race方法,那么一鼓作气再写下resolve和reject吧
这两个方法看起来就非常简单,因为之前的铺垫已经很好了基本属于直接用的状态 还是先来看下用法吧
// 成功 Promise.resolve([1,2,3,4]).then(function(data) { console.log(data); // [1,2,3,4] }); // 失败 Promise.reject('err!').then(function () {}, function (err) { console.log(err); // 'err!' });
来,快点,开写,不犹豫
Promise.resolve = function(value) { return new Promise(function(resolve, reject) { resolve(value); }); }; Promise.reject = function (reason) { return new Promise(function (resolve, reject) { reject(reason); }); };
完成!!!
各位客官,是不是觉得感觉身体被掏空了,写了这么多终于完事了,可以来梳理一下了。 错了,还有最后一个方法没写,它就是defer
defer方法的作用是什么呢?
首先它不需要写new来生成promise实例了,
其次是因为promise的测试库promises-aplus-tests需要写这么一个方法,哈哈哈
看下怎么用吧,其实和Q是一样的
function read() { let defer = Promise.defer(); // Q里写的是Q.defer() require('fs').readFile('./2.promise/1.txt', 'utf8', function (err, data) { if(err) defer.reject(err); defer.resolve(data); }) return defer.promise; } read().then(function (data) { console.log(data) },function(err){ console.log(err); })
为了测试库的需要,实现最后一个吧
Promise.defer = Promise.deferred = function() { let def = {}; def.promise = new Promise(function(resolve, reject) { def.resolve = resolve; def.reject = reject; }); return def; }
终于,完成了所有的实现了。实属不易,也谢谢大家坚持看完,希望有所收获吧!哈哈
该实现是按照promiseA+规范来进行梳理的 当使用promise的时候需要先new一个实例,所以我们要构造一个promise构造函数
let p = new Promise(function(resolve, reject){ // 先调谁,就走谁 reject('不对了'); // 失败 resolve(1); // 成功 // 如果抛出异常也会走到then的失败函数里 throw new Error('错了'); }); p.then(function(data) { console.log(data); }, function(err) { console.log(err); // '不对了' });
new的Promise实例包含一个执行函数(executor),该函数有两个参数,分别是成功和失败的方法
这里要记住一点是成功和失败的方法是互斥的,同时调用时,先调用的先执行,后面调用的不会再执行
状态
都知道promise是有三种状态值的
那么给构造函数写一个status,用来保存状态的变化 pendding可以转为fulfilled或者rejected, 一旦转换了,status就不会改变了,要么是成功态,要么是失败态
// 实现promise function Promise(executor) { const self = this; self.status = 'pending'; // 默认的状态,pending->fulfilled, pending->rejected self.value = null; // 成功的值 self.reason = null; // 失败的原因 function resolve(value) { // 成功 if (self.status === 'pending') { self.status = 'fulfilled'; self.value = value; } } function reject(reason) { // 失败 if (self.status === 'pending') { self.status = 'rejected'; self.reason = reason; } } try { executor(resolve, reject); } catch(e) { // 如果抛出错误,就直接走失败的方法 reject(e); } } Promise.prototype.then = function(onFulfilled, onRejected) { const self = this; if (self.status === 'fulfilled') { onFulfilled(); } if (self.status === 'rejected') { onRejected(); } }; module.exports = Promise;
上面写了很多,但是发现都是处理同步的情况,当遇到异步的时候该怎么处理呢,我们继续往下写
异步promise
promise中可以写异步代码,并且可以进行多次then
let p = new Promise(function(resolve, reject){ // 异步操作 setTimeout(function() { resolve('ok'); }, 1000); }); p.then(function(data) { console.log(data); }, function(err) { console.log(err); }); p.then(function(data) { console.log(data); }, function(err) { console.log(err); });
实现一下异步的promise
promise实例可以多次then,当成功后会将then中的成功的方法依次执行, 我们可以先将then中成功的回调和失败的回调存到数组内,当成功时调用成功的数组即可
function Promise() { self.status = 'pending'; self.value = null; self.reason = null; + self.onFulfilledCb = []; // 存放then成功的回调 + self.onRejectedCb = []; // 存放then失败的回调 function resolve(value) { if (self.status === 'pending') { self.status = 'fulfilled'; self.value = value; + self.onFulfilledCb.forEach(function (fn) { fn(); }); } } function reject(reason) { if (self.status === 'pending') { self.status = 'rejected'; self.reason = reason; + self.onRejectedCb.forEach(function (fn) { fn(); }); } } try { executor(resolve, reject); } catch(e) { // 如果抛出错误,就直接走失败的方法 reject(e); } } Promise.prototype.then = function(onFulfilled, onRejected) { const self = this; if (self.status === 'fulfilled') { onFulfilled(self.value); } if (self.status === 'rejected') { onRejected(self.reason); } // 当调用then时 可能没成功也没失败,就处于pending状态 + if (self.status === 'pending') { // 将成功的回调添加到数组中 self.onFulfilledCb.push(function () { onFulfilled(self.value); }); self.onRejectedCb.push(function () { onRejected(self.reason); }); } }; module.exports = Promise;
链式调用
上面的代码实现了异步调用的时候then方法是怎么处理的情况,
那么接下来就来到重要的环节了,大家都知道promise的then方法也是支持链式调用的 不过then的链式调用可不同于jquery返回this的方式实现的
⭐️ 它是返回了一个新的promise
下面来看代码
let p = new Promise(function(resolve, reject) { resolve('ok'); }); p.then(function (data) { console.log(data); return data; }, function (err) { console.log(err); // return '有返回值就走成功态' }).then(function (data) { console.log('2 '+data); }, function (err) { console.log(err); })
now让我们开始实现链式调用,还是老样子,+号代表实现的主要代码:
Promise.prototype.then = function (onFulfilled, onRejected) { const self = this; + let promise2; // 定义一个promise2变量 if (self.status === 'fulfilled') { // 返回一个新的promise + promise2 = new Promise(function (resolve, reject) { // x可能是个普通值,也可能是个promise + let x = onFulfilled(self.value); // x也可能是别人写的promise,写一个函数统一去处理 + resolvePromise(promise2, x, resolve, reject); }); } if (self.status === 'rejected') { + promise2 = new Promise(function (resolve, reject) { + let x = onRejected(self.reason); + resolvePromise(promise2, x, resolve, reject); }); } // 当调用then时 可能没成功也没失败,就处于pending状态 if (self.status === 'pending') { // 将成功的回调添加到数组中 + promise2 = new Promise(function (resolve, reject) { self.onFulfilledCb.push(function () { + let x = onFulfilled(self.value); + resolvePromise(promise2, x, resolve, reject); }); self.onRejectedCb.push(function () { + let x = onRejected(self.reason); + resolvePromise(promise2, x, resolve, reject); }); }); } return promise2; }; function resolvePromise(p2, x, resolve, reject) { if (p2 === x) { // 不能返回自己 return reject(new TypeError('循环引用')); } let called; // 表示是否调用成功or失败 // x返回的可能是对象和函数也可能是一个普通的值 if (x !== null && (typeof x === 'object' || typeof x === 'function')) { try { let then = x.then; if (typeof then === 'function') { then.call(x, function (y) { // 防止多次调用 if (called) return; called = true; // y可能还是个promise,所以递归继续解析只到返回一个普通值 resolvePromise(p2, y, resolve, reject); }, function (e) { if (called) return; called = true; reject(e); }); } } catch(e) { if (called) return; called = true; reject(e); } } else { resolve(x); // 返回一个普通值 } }
通过以上代码也可以得出了一些结论
以上我们已经实现了promise的链式调用了,但这还不够promise有一种情况是在then中什么都不传的情况,还继续链式调用
let p = new Promise(function (resolve, reject) { reject(999); }); p.then().then().then(function (data) { console.log(data); // 999 }, function (err) { console.log(err); });
这就是promise中值的穿透,该实现是在then中,那么我们也对then方法加以改良去实现一下
Promise.prototype.then = function(onFulfilled, onRejected) { + onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) { return value }; + onRejected = typeof onRejected === 'function' ? onFulfilled : function (err) { throw err }; const self = this; let promise2; };
其实很简单,就是在then的时候如果你不给我传回调函数,那么我就判断一下,确实没有传,那我就给你新创建一个函数然后在成功回调的时候把值传到下一个then中去
另外在promise规范中要求,所有的onFulfilled和onRejected都需要异步执行,所以我们要给他们加上异步,显然这个更容易实现了,看下面代码
Promise.prototype.then = function(onFulfilled, onRejected) { if (self.status === 'fulfilled') { promise2 = new Promise(function (resolve, reject) { // 当成功或失败执行时,有异常那么返回的promise应该处于失败态 // 用try/catch来防止返回异常的情况 // 加上setTimeout实现异步调用 + setTimeout(function () { try { let x = onFulfilled(self.value); resolvePromise(promise2, x, resolve, reject); } catch(e) { reject(e); } }); }); } if (self.status === 'rejected') { promise2 = new Promise(function (resolve, reject) { + setTimeout(function () { try { let x = onRejected(self.reason); resolvePromise(promise2, x, resolve, reject); } catch(e) { reject(e); } }); }); } }; if (self.status === 'pending') { // 将成功的回调添加到数组中 promise2 = new Promise(function (resolve, reject) { self.onFulfilledCb.push(function () { + setTimeout(function () { try { let x = onFulfilled(self.value); resolvePromise(promise2, x, resolve, reject); } catch(e) { reject(e); } }); }); self.onRejectedCb.push(function () { + setTimeout(function () { try { let x = onRejected(self.reason); resolvePromise(promise2, x, resolve, reject); } catch(e) { reject(e); } }); }); } return promise2;
好了,我们以上就实现了promise中最重要的then方法了,写的不好请多理解吧。
不过既然把then都写完了,那接下来再写几个其他的,写多点也让大家一起来研究研究。
其他方法
捕获错误的方法
先来看下用法,其实这个和then方法里失败的回调onRejected是一样的
let p = new Promise(function(resolve, reject) { reject('错错错'); }); p.then(function(data){ console.log(data); }).catch(function(e){ console.log(e); // '错错错' });
下面上代码:
// 捕获错误的方法 Promise.prototype.catch = function(callback) { // 也是调用then方法,给成功的回调传一个null,给失败的回调传入callback return this.then(null, callback); };
Promise类自身也有一些方法,常用的有all, race, resolve, reject
都写到这里了,那就继续写下去,废话不多说,开撸!!!
all方法
Promise.all()的用法
let p = new Promise(function(resolve, reject) { setTimeout(function() { resolve('北京'); }, 1000); }); let p2 = new Promise(function(resolve, reject) { setTimeout(function() { resolve('南京'); }, 200); }); let p3 = new Promise(function(resolve, reject) { resolve('东京'); }); Promise.all([p, p2, p3]).then(function(data) { // all方法是所有的promise都成功后才会成功 // 按照传入all里的顺序依次执行,p里面的定时器只是执行完成的时间而已,不影响all里输出顺序 // 如:p这个需要等1秒后才会返回成功态,p2就需要等待1秒时间 console.log(data); // [ '北京', '南京', '东京' ] });
上面知道了用法,下面就来写实现,客官且留步继续看下去
Promise.all = function(items) { return new Promise(function(resolve, reject) { let res = []; // 用来存储成功函数返回的值 let num = 0; // 记录都返回成功的数字 let len = items.length; // 数组的长度 // 对数组进行遍历 for (let i = 0; i < len; i++) { items[i].then(function(data) { res[i] = data; if (++num === len) { resolve(res); } }, reject); } }); };
如此这般,这般如此,就实现了all
再接再厉还有race方法
那么race方法顾名思义,race就是比赛,就是在比谁快,谁先成功,返回的就是谁的成功数据
race方法
来看一下怎么用
let p = new Promise(function (resolve, reject) { setTimeout(function () { resolve('北京'); }, 1000); }); let p2 = new Promise(function (resolve, reject) { setTimeout(function () { resolve('南京'); }, 500); }); let p3 = new Promise(function (resolve, reject) { setTimeout(function () { resolve('东京'); }); }); Promise.race([p, p2, p3]).then(function (data) { // p3最先执行完返回成功,所以data就为p3的值,其他的p,p2都不返回 console.log(data); // '东京' });
那么,不啰嗦了,写吧
Promise.race = function(items) { return new Promise(function(resolve, reject) { for (let i = 0; i < items.length; i++) { items[i].then(resolve, reject); } }); };
以上就实现了race方法,那么一鼓作气再写下resolve和reject吧
这两个方法看起来就非常简单,因为之前的铺垫已经很好了基本属于直接用的状态 还是先来看下用法吧
resolve和reject方法
// 成功 Promise.resolve([1,2,3,4]).then(function(data) { console.log(data); // [1,2,3,4] }); // 失败 Promise.reject('err!').then(function () {}, function (err) { console.log(err); // 'err!' });
来,快点,开写,不犹豫
Promise.resolve = function(value) { return new Promise(function(resolve, reject) { resolve(value); }); }; Promise.reject = function (reason) { return new Promise(function (resolve, reject) { reject(reason); }); };
完成!!!
各位客官,是不是觉得感觉身体被掏空了,写了这么多终于完事了,可以来梳理一下了。 错了,还有最后一个方法没写,它就是defer
defer方法的作用是什么呢?
首先它不需要写new来生成promise实例了,
其次是因为promise的测试库promises-aplus-tests需要写这么一个方法,哈哈哈
看下怎么用吧,其实和Q是一样的
function read() { let defer = Promise.defer(); // Q里写的是Q.defer() require('fs').readFile('./2.promise/1.txt', 'utf8', function (err, data) { if(err) defer.reject(err); defer.resolve(data); }) return defer.promise; } read().then(function (data) { console.log(data) },function(err){ console.log(err); })
为了测试库的需要,实现最后一个吧
Promise.defer = Promise.deferred = function() { let def = {}; def.promise = new Promise(function(resolve, reject) { def.resolve = resolve; def.reject = reject; }); return def; }
终于,完成了所有的实现了。实属不易,也谢谢大家坚持看完,希望有所收获吧!哈哈