本文根据promise的用法, 带您一步一步的了解promise的源码内幕; 本文采用要点、使用、源码分析的思路,步步为营,一点一点的剖析Promise的神秘世界;
本文Promise源码部分采用es5语法
Promise定义
- promise是异步解决方案; 字面意思是"承诺",即未来某个时刻才能得到结果;
- 从语法上来说,它是一个对象,从它可以获取异步操作的消息;
基本用法 - 构造函数
ES6 规定,Promise对象是一个构造函数,用来生成Promise实例
- 使用:
let p = new Promise();复制代码
- 源码
function Promise(){ //... }复制代码
- 解析: 构造函数与普通的函数并无二致, 只是我们在使用时需要通过new的方式来调用; 其中构造函数中的this指向new生成的对象实例;
Promise构造函数接收一个执行器函数(executor)作为参数, 该函数的两个参数分别为resolve和reject。当new生成对象实例的时候,Promise的参数executor会立即执行
- 使用:
let p = new Promise((resolve, reject)=>{ // coding.... });复制代码
- 源码:
function Promise(executor){ executor(resolve, reject); // 该函数会在new Promise()实例时立即调用,它接收两个参数: resolve reject }复制代码
- 解析: executor即调用时候传递进来的函数
(resolve, reject)=>{ // coding... }
, 调用时会传递两个参数进去, 供// coding....处使用
执行器executor的参数(resolve, reject)是两个函数, 这两函数的作用:
- resolve函数是将Promise对象的状态从"pending"变为"resolved"('未完成' => '成功'), 在异步操作成功时调用,并将异步操作结果作为参数传递出去(源码的resolve函数中接收);
- reject函数 是将Promise对象的状态从"pending"变为"rejected"('未完成' => '失败'), 在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去(源码的reject函数中接收);
- 使用:
let p = new Promise((resolve, reject)=>{ // coding.... resolve('成功'); // 或 reject('失败'); }); 复制代码
- 源码:
function Promise(executor){ function resolve(value){ console.log(value); // 调用resolve('成功'); => 成功 } function reject(reason){ console.log(reason); // 调用reject('失败'); => 失败 } executor(resolve, reject); // 该函数会在new Promise()实例时立即调用,它接收两个参数: resolve reject }复制代码
// 升级(添加状态) function Promise(executor){ this.status = 'pending'; // 用于保存promise实例的状态,默认为pending,等待态 this.value = undefined; // 保存成功时的值 this.reason = undefined; // 保存失败时的原因 function resolve(value){ console.log(value); // 调用resolve('成功'); => 成功 this.value = value; } function reject(reason){ console.log(reason); // 调用reject('失败'); => 失败 this.reason = reason; } executor(resolve, reject); // 该函数会在new Promise()实例时立即调用,它接收两个参数: resolve reject }复制代码
// 升级(状态的转变) pending => fulfilled 或 pending => rejected //一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果[摘自:http://es6.ruanyifeng.com/#docs/promise] function Promise(executor){ let self = this; this.status = 'pending'; // pending => fulfilled | rejected this.value = undefined; this.reason = undefined; // 成功 function resolve(value){ if(self.status === 'pending'){ self.value = value; self.status = 'fulfilled'; console.log(self.status) } } function reject(reason){ if(self.status === 'pending'){ self.reason = reason; self.status = 'rejected'; console.log(self.reason) } } // 需要通过别名引用的方式; 因为resolve和reject中的this指向的是全局对象 executor(resolve, reject); // 该函数会在new Promise()实例时立即调用,它接收两个参数: resolve reject }复制代码
-
解析:
- promise有三个状态: pending(进行中或等待中)、fulfilled(已成功)、rejeced(已失败)
- 使用时调用resolve函数,会把promise的状态转变为fulfilled,并且将成功的结果保存到promise实例的value属性
- 使用时调用reject函数,会把promise的状态转变为rejected,并且将失败的原因保存到promise实例的reason属性
- 状态的转变只能从pending => fulfilled 或 rejected, fulfilled和rejected之间是不能转换的;所以在resolve和rejected函数中添加了状态判断
- 调用resolve函数或reject函数会修改promise实例value(成功的结果)属性或reason(失败的原因),而此时在resolve和reject内部如何引用到promsie实例呢? -> 在Promise构造函数内部设置this的引用: let self = this; 在resolve或reject函数中,通过self引用promise实例;
-
小结: 代码书写到此刻,我们打印Promise实例, 即可查看到当前实例的状态和相关属性;
let p = new Promise((resolve, reject)=>{ resolve('成功'); /* if (/* 异步操作成功 */){ resolve(value); } else { reject(error); } */});console.log(p); // Promise { status: 'fulfilled', value: '成功', reason: undefined }复制代码
如果Promise的执行器函数(executor)里面直接抛出异常呢? 捕获错误,相对于执行了reject
- 使用:
let p = new Promise((resolve, reject)=>{ throw new Error('错误扔给你, 来打我呀^_^');});复制代码
- 源码:
function Promise (executor){ // 在promise内部定义一个状态 当前promise的状态 let self = this; self.value = undefined; self.reason = undefined self.status = 'pending'; // 默认promise的状态是pengding self.onResolevedCallbacks = []; // 存放所有成功的回调 self.onRejectedCallbacks = []; // 存放所有失败的回调 function resolve(value){ // (value!=null && typeof value === 'object') || typeof value == 'function' if(value instanceof Promise){ console.log('here'); // if(value.then && typeof value.then === 'function'){ return value.then((data)=>{ console.log('11111111111') console.log(data) resolve(data) },y=>{ reject(y); }); // } } console.log(self.status) if(self.status === 'pending'){ self.value = value; console.log('pending:',value) self.status = 'resolved'; // 成功态 console.log('pending--', self.onResolevedCallbacks) self.onResolevedCallbacks.forEach(fn=>fn()); } } function reject(reason){ if(self.status === 'pending'){ self.reason = reason; self.status = 'rejected'; // 失败态 // 发布 self.onRejectedCallbacks.forEach(fn =>fn()); } } // ---------------修改在这里------------------------------------------------------------------------- // 小样,敢给大爷抛错误, 直接捕获了,让你的错误销声匿迹 @<_>@ try{ executor(resolve,reject); // 用户会调用resolve || reject }catch(e){ reject(e); // 说明失败了 }}复制代码
- 解析:
上面代码理解了,请移步下一个关 注意,为了行文方便,后面的resolved统一只指fulfilled状态,不包含rejected状态
Promise.prototype.then()
Promise.prototype.then()方法是Promise的核心方法; Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数;then方法的作用就是为promise实例添加状态改变时的回调函数;
- 使用:
let p = new Promise((resolve, reject)=>{ resolve('成功'); // reject('失败');});p.then((value)=>{ // 成功时执行的回调 console.log('执行resolve,输出实例成功态的vlaue属性: ', value);}, (reason)=>{ // 失败时执行的回调 console.log('执行reject,输出实例失败态的reason属性: ', reason);});// => 执行resolve,输出实例成功态的vlaue属性: 成功复制代码
- 源码:
function Promise(executor){ let self = this; self.status = 'pending'; // pending => fulfilled | rejected self.value = undefined; self.reason = undefined; // 成功 function resolve(value){ if(self.status === 'pending'){ self.value = value; self.status = 'fulfilled'; } } function reject(reason){ if(self.status === 'pending'){ self.reason = reason; self.status = 'rejected'; } } try { executor(resolve, reject); // 该函数会在new Promise()实例时立即调用,它接收两个参数: resolve reject }catch(err){ reject(err); }}// Promise的核心方法Promise.prototype.then = function(onFulfilled, onRejected){ if(this.status === 'fulfilled'){ onFulfilled(this.value); // 这里传递成功的值 } if(this.status === 'rejected'){ onRejected(this.reason); // 这里传递失败的原因或错误 }};复制代码
- 解析:
- then方法可以接收两个回调函数作为参数; 1> Promise实例对象的状态变为resolved时调用 2> Promsie实例的状态变为rejected时调用[可选];
- then的成功回调函数接收promise对象(也就是promise实例)的成功时的值作为参数(用于处理->执行);
- then的失败回调函数接收promise对象的失败时的原因或错误作为参数;
Promise.prototype.then() 与异步调用; 我们知道Promise主要是用来处理异步的, 当promise对象的状态在一段时间后发生变化时, 就会触发then方法绑定的回调函数; 这里要注意几点:
1> then方法在调用时会立即执行(同步), 关键在于then不同状态的回调函数只有在状态发生改变时执行(异步); 2> Promise是同步的, 实例新建后会立即执行; then是异步的,当状态改变时才执行复制代码
let promise = new Promise(function(resolve, reject) { console.log('Promise'); resolve(); }); promise.then(function() { console.log('resolved.'); }); console.log('Hi!'); // => 依次输出: 'Promise' 'Hi!' 'resolved.'复制代码
- 使用:
let p = new Promise((resolve, reject)=>{ setTimeout(()=>{ resolve('成功'); // 2秒钟之后,让promise的状态变成成功态 }, 2000);});p.then((value)=>{ // 成功时执行的回调 console.log('2秒后执行resolve,输出实例成功态的vlaue属性: ', value);}, (reason)=>{ // 失败时执行的回调 console.log('执行reject,输出实例失败态的reason属性: ', reason);});复制代码
- 源码:
function Promise(executor){ let self = this; self.status = 'pending'; // pending => fulfilled | rejected self.value = undefined; self.reason = undefined; self.onFulfilledCallbacks = []; // 用于存放所有then方法成功态的回调 self.onRejectedCallbacks = []; // 用于存放所有then方法失败态的回调 // 成功 function resolve(value){ if(self.status === 'pending'){ self.value = value; self.status = 'fulfilled'; self.onFulfilledCallbacks.forEach(fn=>fn()); // 当状态变为resolved时执行订阅的函数(then方法成功时的回调) } } function reject(reason){ if(self.status === 'pending'){ self.reason = reason; self.status = 'rejected'; self.onRejectedCallbacks.forEach(fn=>fn()); // 当状态变为rejected时执行订阅的函数(then方法失败时的回调) } } try { executor(resolve, reject); // 该函数会在new Promise()实例时立即调用,它接收两个参数: resolve reject }catch(err){ reject(err); } } Promise.prototype.then = function(onFulfilled, onRejected){ if(this.status === 'fulfilled'){ onFulfilled(this.value); // 这里传递成功的值 } if(this.status === 'rejected'){ onRejected(this.reason); // 这里传递失败的原因或错误 } // 在异步执行完成之前,promise的状态为pending,此时会把then两种状态的回调先存储起来,待状态改变后执行 if(this.status === 'pending'){ this.onFulfilledCallbacks.push(()=>{ onFulfilled(this.value); }); this.onRejectedCallbacks.push(()=>{ onRejected(this.reason); }); } };复制代码
-
解析:
- 回调的存储和调用是一个典型的发布/订阅模式;promise处于等待态时订阅,状态改变后执行发布;
- 在异步执行完成之前,此时promise的状态为pending,此时我们把then函数的参数(成功的回调resove和失败reject的回调)存储起来,待异步完成状态改变后调用;
- 用于存储then回调的是两个数组,一个是成功回调函数的数组,一个是失败回调函数的数组;
- 如果调用resolve函数和reject函数时带有参数, 那么它们的参数会被传递给then的回调函数; 如果没传具体的参数,那就将undefined传递过去;
- reject函数的参数通常是Error实例,表示抛出异常或错误;
- resolve函数的参数除了普通值以外, 还可以是另外一个Promise
then方法返回的是一个新的Promise实例(该实例不是原来那个Promise实例);
- 使用(同步情况下的链式调用):
let p = new Promise((resolve, reject)=>{ resolve(420); }); p.then((value)=>{ // 成功时执行的回调 console.log('2秒后执行resolve,输出实例成功态的vlaue属性: ', value); // 2秒后执行resolve,输出实例成功态的vlaue属性: 420 return value + 100; }, (reason)=>{ // 失败时执行的回调 console.log('执行reject,输出实例失败态的reason属性: ', reason); }).then((value)=>{ // then的成功回调里面输出,前一个then成功回调的返回值(即promise实例的value属性), 如果没有显示的return, 那么返回的是undefined console.log('第二个then的成功回调里面输出,前一个then成功回调的返回值: ',value); // 第二个then的成功回调里面输出,前一个then成功回调的返回值: 520 return value + 200; }).then((value)=>{ console.log('第三个then的成功回调里面输出,前一个then成功回调的返回值: ',value); // 第三个then的成功回调里面输出,前一个then成功回调的返回值: 720 });复制代码
- 源码:
function Promise(executor){ let self = this; self.status = 'pending'; // pending => fulfilled | rejected self.value = undefined; self.reason = undefined; self.onFulfilledCallbacks = []; // 用于存放所有then方法成功态的回调 self.onRejectedCallbacks = []; // 用于存放所有then方法失败态的回调 // 成功 function resolve(value){ if(self.status === 'pending'){ self.value = value; self.status = 'fulfilled'; self.onFulfilledCallbacks.forEach(fn=>fn()); } } function reject(reason){ if(self.status === 'pending'){ self.reason = reason; self.status = 'rejected'; self.onRejectedCallbacks.forEach(fn=>fn()); } } try { executor(resolve, reject); // 该函数会在new Promise()实例时立即调用,它接收两个参数: resolve reject }catch(err){ reject(err); } } Promise.prototype.then = function(onFulfilled, onRejected){ let self = this; let promise2 = new Promise((resolve, reject)=>{ console.log(this === self); // 这里因为使用了箭头函数,所以self === this, 指向同一个对象; 为了不混淆,后面将采用self的方式 // 该executor函数会里面执行,因此把之前的状态判断及成功回调的代码移到此处与之前功能一样; // 需要说明的是这个promise需要根据上一个then的状态和值进行判断,故而设置self变量用于引用上一个this if(this.status === 'fulfilled'){ let x = onFulfilled(this.value); // 成功回调; 这里的value指的是第一个Promise实例的value属性 resolve(x); // 将处理后的结果作为参数传递给promise实例的value属性; 同时也会传递给下一个then的成功回调 } if(this.status === 'rejected'){ try{ let x = onRejected(this.reason); // onRejected处理 reject(x); }catch(e){ reject(e); } } if(this.status === 'pending'){ this.onFulfilledCallbacks.push(()=>{ onFulfilled(this.value); }); this.onRejectedCallbacks.push(()=>{ onRejected(this.reason); }); } }); return promise2; };复制代码
-
解析:
- then链式调用的关键是:then方法调用后返回一个新的Promise实例;
- 生成promise2实例时传递到Promise构造函数中的执行器excutor, 它里面用于判断状态的是上一个promise实例的状态,操作的都是上一个promise实例的属性;
- 有多少个then,就至少有多少个promise实例;
- 不论有多少个promise实例,在new新的Promise实例时都是判断前一个promise实例的状态、操作前一个promise实例的属性, 把前面的promise实例上的属性更新后, 传递到后面的then回调函数里面;
- 无论多少个then,都是前一个回调函数完成以后,将返回结果作为参数,传入下一个then的对应的回调中;
- 无论多少个then,某一状态的回调都是刚在一个数组,然后挨个的执行; 前一个执行完成后把实例属性传递给下一个回调函数
Promise.prototype.then() 与异步调用
- 使用(异步情况下的链式调用):
let p = new Promise((resolve, reject)=>{ setTimeout(()=>{ resolve(420); }, 3000);});p.then((value)=>{ // 成功时执行的回调 console.log('2秒后执行resolve,输出实例成功态的vlaue属性: ', value); // 2秒后执行resolve,输出实例成功态的vlaue属性: 420 return value + 100;}, (reason)=>{ // 失败时执行的回调 console.log('执行reject,输出实例失败态的reason属性: ', reason); throw new Error('失败了');}).then((value)=>{ // then的成功回调里面输出,前一个then成功回调的返回值(即promise实例的value属性), 如果没有显示的return, 那么返回的是undefined console.log('第二个then的成功回调里面输出,前一个then成功回调的返回值: ',value); // 第二个then的成功回调里面输出,前一个then成功回调的返回值: 520 return value + 200;}, (reason)=>{ console.log('第二个then的失败: ', reason);}).then((value)=>{ console.log('第三个then的成功回调里面输出,前一个then成功回调的返回值: ',value); // 第三个then的成功回调里面输出,前一个then成功回调的返回值: 720});// => 2秒后执行resolve,输出实例成功态的vlaue属性: 420 第二个then的成功回调里面输出,前一个then成功回调的返回值: 520 第三个then的成功回调里面输出,前一个then成功回调的返回值: 720复制代码
- 源码
function Promise(executor){ let self = this; self.status = 'pending'; // pending => fulfilled | rejected self.value = undefined; self.reason = undefined; self.onFulfilledCallbacks = []; // 用于存放所有then方法成功态的回调 self.onRejectedCallbacks = []; // 用于存放所有then方法失败态的回调 // 成功 function resolve(value){ if(self.status === 'pending'){ self.value = value; self.status = 'fulfilled'; self.onFulfilledCallbacks.forEach(fn=>fn()); } } function reject(reason){ if(self.status === 'pending'){ self.reason = reason; self.status = 'rejected'; self.onRejectedCallbacks.forEach(fn=>fn()); } } try { executor(resolve, reject); // 该函数会在new Promise()实例时立即调用,它接收两个参数: resolve reject }catch(err){ reject(err); } } Promise.prototype.then = function(onFulfilled, onRejected){ let self = this; let promise2 = new Promise(function(resolve, reject){ // console.log(this === self); // 这里因为使用了箭头函数,所以self === this, 指向同一个对象 // 该executor函数会里面执行,因此把之前的状态判断及成功回调的代码移到此处与之前功能一样; // 需要说明的是这个promise需要根据上一个的状态和值进行判断,故而设置self变量用于引用上一个this if(self.status === 'fulfilled'){ let x = onFulfilled(self.value); // 成功回调; 如果没有就当前对象的value属性值 resolve(x); // 将处理后的结果作为参数传递给promise实例的value属性; 同时也会传递给下一个then的成功回调 } if(self.status === 'rejected'){ try{ let x = onRejected(self.reason); // onRejected处理 reject(x); }catch(e){ reject(e); } } if(self.status === 'pending'){ self.onFulfilledCallbacks.push(()=>{ let x = onFulfilled(self.value); resolve(x); }); self.onRejectedCallbacks.push(()=>{ let x = onRejected(self.reason); reject(x); }); } }); return promise2; };复制代码
-
解析
- 异步和同步的处理方式几乎一致, 异步的无非就是在pending态时先把回调保存起来,待状态改变时再执行
Promise.prototype.then()的成功回调返回一个新的promise实例和执行executor时resolve一个新的promsie实例
采用链式的then,可以指定一组按照次序调用的回调函数; 此时前一个回调函数,有可能返回的还是一个Promise对象(即有异步操作),这时后一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用;
因此处逻辑\代码比较多,放在一起看了
- 使用
let p = new Promise((resolve, reject)=>{ resolve(1000); // 或 resolve(new Promise((resolve, reject)=>{ resolve('成功');})) }); p.then((value)=>{ return new Promise((resolve, reject)=>{ resolve(value + 500); }); }).then((value)=>{ console.log(value); // 1500 });复制代码
- 源码
function Promise(executor){ let self = this; this.status = 'pending'; // pending => fulfilled | rejected this.value = undefined; this.reason = undefined; this.onFulfilledCallbacks = []; // 用于存放所有then方法成功态的回调 this.onRejectedCallbacks = []; // 用于存放所有then方法失败态的回调 // 成功 function resolve(value){ // 如果value是个Promise实例, 就要先处理该promise实例 /* let p = new Promise((resolve, reject)=>{ resolve(new Promise(function(resolve, reject){ resolve('成功'); })); }) */ if(value instanceof Promise){ return value.then((data)=>{ resolve(data); },(y)=>{ reject(y); }); } if(self.status === 'pending'){ self.value = value; self.status = 'fulfilled'; self.onFulfilledCallbacks.forEach(fn=>fn()); } } function reject(reason){ if(self.status === 'pending'){ self.reason = reason; self.status = 'rejected'; self.onRejectedCallbacks.forEach(fn=>fn()); } } try { executor(resolve, reject); // 该函数会在new Promise()实例时立即调用,它接收两个参数: resolve reject }catch(err){ reject(err); } } function resolvePromise(x, promise2, resolve, reject){ // 如果then的回调函数中返回之前的promsie,就有问题了(因为状态一旦改变就被冻结,不能再次变化)) if(x === promise2){ return reject(new TypeError('循环应用')); } // 判断x是普通值还是对象,如果是普通值,直接resolve // 如果是对象或函数执行 if((x !== null && typeof x === 'object') || typeof x === 'function'){ // 这里可以是promise,然后尝试执行 try { // 判断有没有then方法, 在获取then的过程中也可能会出错(比如某个对象的then属性get的时候抛出错误) /* let obj = {}; Object.defineProperty(obj, 'then', { get(){ throw new Error('不让你get!'); } }); */ let then = x.then; // 获取x的then属性; 如果没有就会抛出错误 if(typeof then === 'function'){ then.call(x, (y)=>{ // y有可能也是一个promise // 递归解析,直到结果是普通值为止 resolvePromise(y, promise2, resolve, reject); }, (r)=>{ reject(r); }); } else { // 有可能是普通对象或普通值 { then: 'xbs'}或{ then: {}} resolve(x); } }catch(e){ // 没有then那可能就是一个普通对象{a:xxx} reject(e); } } else { resolve(x); } } Promise.prototype.then = function(onFulfilled, onRejected){ // .then().then().then()值的穿透, 因为我们在then没传回调参数时,手动给其添加了相应的回调函数 onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value=> value; onRejected = typeof onRejected === 'function' ? onRejected: err => {throw err}; let self = this; let promise2 = new Promise(function(resolve, reject){ // 该executor函数会立刻执行,因此把之前的状态判断及成功回调的代码移到此处与之前功能一样; // 需要说明的是这个promise需要根据上一个的状态和值进行判断,故而设置self变量用于引用上一个this if(self.status === 'fulfilled'){ // 这里要使用promise2, 所以需要增异步保证可以获取到promise2; // 为什么要使用promise2? - 因为每次then要返回的是一个新的promise, 如果有人要返回上一个promise呢, 这时候就需要去判断,promise2和x的关系 /* // 看这里就知道为什么要判断x与promise2的关系了 let p = new Promise((resolve,reject)=>{ resolve('成功'); }) p.then((value)=>{ return p; }); */ // 使用定时器是为了保障promise2能获取到; (先执行同步代码)异步代码(setTimeout是宏任务,主栈代码执行完毕,微任务执行后再执行)是在同步执行完成后执行,故而可以获取到promise2 setTimeout(function(){ // 为什么要try呢? 因为没人能保证樱花大道上没有狗狗的翔 /* let p = new Promise((resolve,reject)=>{ resolve('成功'); }) p.then((value)=>{ throw new Error('就欺负你怎么了'); // 成功态中抛错误,可谓防不胜防啊 }, (reason)=>{ // 失败回调... }) */ try { let x = onFulfilled(self.value); // 如果此次x是返回的新的promise如何处理? // 采用统一的方法来处理x,判断x是promise还是普通值 resolvePromise(x, promise2, resolve, reject); } catch(err) { // 如果执行函数时抛出失败 那么会走向下一个then的失败状态 reject(err); } }, 0); } if(self.status === 'rejected'){ setTimeout(function(){ try { let x = onRejected(self.reason); // onRejected处理 resolvePromise(x, promise2, resolve, reject); } catch(err) { // 如果执行函数时抛出失败 那么会走向下一个then的失败状态 reject(err); } }, 0) } if(self.status === 'pending'){ // 异步的处理在这里 // 因为需要待异步执行完成后调用执行,而何时调用并不知道; 因此要先存起来(订阅),待状态改变再执行(发布) self.onFulfilledCallbacks.push(()=>{ setTimeout(()=>{ // 同样也会遇到成功态回调里面抛出错误的情况,所以也要try{}catch(){}一下 try{ let x = onFulfilled(self.value); resolve(x); }catch(err){ reject(err); } },0); }); self.onRejectedCallbacks.push(()=>{ setTimeout(()=>{ // 同样也会遇到成功态回调里面抛出错误的情况,所以也要try{}catch(){}一下 try{ let x = onRejected(self.reason); reject(x); }catch(err){ reject(err); } },0); }); } }); return promise2; };复制代码
- 解析
其实到这里,Promise的核心已经实现了
Promise.prototype.catch()
Promise.prototype.catch方法是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数
- 使用
getJSON('/posts.json').then(function(posts) { // ... }).catch(function(error) { // 处理 getJSON 和 前一个回调函数运行时发生的错误 console.log('发生错误!', error); });复制代码
- 源码
Promise.prototype.catch = function(errCallback){ return this.then(null, errCallback); };复制代码
Promise.all
Promise.all方法用于将多个Promise实例,包装成一个新的Promise实例
- 使用
let p1 = new Promise((resolve, reject)=>{ resolve('p1-success!'); }); let p2 = new Promise((resolve, reject)=>{ setTimeout(()=>{ resolve('p2-success!'); }, 2000); }); let p3 = new Promise((resolve, reject)=>{ setTimeout(()=>{ resolve('p3-success!'); }, 3000); }); let p = Promise.all([p1, p2, p3]).then((result)=>{ console.log(result); // [ 'p1-success!', 'p2-success!', 'p3-success!' ] });复制代码
- 源码
Promise.all = function(values){ return new Promise(function(resolve, reject){ let results = []; // 存放结果 let index = 0; // 处理方法执行了几次 function processData(resIndex, data){ index++; results[resIndex] = data; // 将执行结束后的结果存放到结果数组(因为结果和执行顺序有严格对应关系,所以不能用push,用arr[0] = value的形式); if(index === values.length){ resolve(results); // 当结果数组和执行操作的数量一样时,将结果返回 } } for(let i = 0; i < values.length; i++){ let current = values[i]; if(current && current.then && typeof current.then === 'function'){ // promise current.then(y=>{ processData(i, y); },reject) } else { processData(i, current); // 如果是普通值,直接返回 } } }); };复制代码
-
解析
- Promise.all的实现原理的核心是计数器
Promise.race
- 使用
let p1 = new Promise((resolve, reject)=>{ resolve('p1-success!'); }); let p2 = new Promise((resolve, reject)=>{ setTimeout(()=>{ resolve('p2-success!'); }, 2000); }); let p3 = new Promise((resolve, reject)=>{ setTimeout(()=>{ resolve('p3-success!'); }, 3000); }); let p = Promise.race([p1, p2, p3]).then((result)=>{ console.log(result); //'p1-success!' });复制代码
- 源码
Promise.race = function(values){ return new Promise((resolve, reject)=>{ for(let i = 0; i < values.length; i++){ let current = values[i]; if(current && current.then && typeof current.then === 'function'){ current.then(resolve,reject); } else { resolve(current); } } }); };复制代码
Promise.resolve()
Promise.resolve 可以将现有对象转换为Promise对象
- 使用
let p = Promise.resolve(300); console.log(p instanceof Promise); // true复制代码
- 源码
Promise.resolve = function(value){ return new Promise(function(resolve,reject){ resolve(value); }); };复制代码
Promise.reject()
Promise.reject 可以将现有对象转换为Promise对象
- 使用
let p = Promise.reject(300); console.log(p instanceof Promise); // true复制代码
- 源码
Promise.reject = function(reason){ return new Promise(function(resolve,reject){ reject(reason); }); };复制代码
结语
先告一段落啦, 因理解能力有限, 难免会有遗漏和偏差,如果您发现了请告知! 学习和成长的路上看了很多大佬的博客\视频\文档\代码,一直在消费大佬的辛苦成果,自己写一写,算是向大佬致敬了!