Promise也叫期约函数,是ES6中新增的特性,是解决异步编程的一种方案,取代回调函数避免回调地狱。
const p = new Promise((resolve,reject)=>{resolve(1);
});// 链式调用
p.then(res => Promise.resolve(res + 2)).then(res => Promise.resolve(res + 3)).then(res => console.log(res));// 6
Promise的一些特性和约定:
Promse的实现思路
Promise的核心就是通过resolve/reject函数获取到value/reason,通过then函数获得回调函数,然后将value传入回调函数执行,通过then函数保证执行顺序。具体过程如下:
Promise本质上就是通过then函数将callback串联执行,这点和compose有点类似。
function MyPromise(executor) {this.value = undefined;this.reason = undefined;this.state = 'pending';this.onResolveCllbacks = [];this.onRejectCllbacks = [];const resolve = (val) => {if (this.state === 'pending') {this.value = val;this.state = 'fullfilled';// 执行保存的回调if (this.onResolveCllbacks.length > 0) {this.onResolveCllbacks.forEach(fn => fn(this.value));this.onResolveCllbacks = [];};}}const reject = (reason) => {if (this.state === 'pending') {this.reason = reason;this.state = 'reject';// 执行保存的回调if (this.onRejectCllbacks.length > 0) {this.onRejectCllbacks.forEach(fn => fn(this.reason));this.onRejectCllbacks = [];};}}executor(resolve, reject);};
then函数接收onResolve和onReject两个参数作为回调函数,并返回一个新的Promise,返回Promise的规则如下:
MyPromise.prototype.then = function (onResolve, onReject) {if (this.state === 'fullfilled') {if (typeof onResolve === 'function') {const result = onResolve?.(this.value);return result instanceof MyPromise ? result : result ? new MyPromise((resolve, reject) => resolve(result)) : this;} else {return this;};};if (this.state === 'reject') {if (typeof onReject === 'function') {const result = onReject?.(this.reason);return result instanceof MyPromise ? result : result ? new MyPromise((resolve, reject) => reject(result)) : this;} else {return this;}};// 如果此时还没有通过resolve得到value就存到数组中,等到resolve的时候执行if (this.state === 'pending') {return new MyPromise((resolve, reject) => {typeof onResolve === 'function' && this.onResolveCllbacks.push((value) => {const result = onResolve(value);if (result instanceof MyPromise) {result.then(res => resolve(res), reason => reject(reason));} else {resolve(result);};});typeof onReject === 'function' && this.onRejectCllbacks.push((reason) => {const result = onReject(reason);if (result instanceof MyPromise) {result.then(res => resolve(res), reason => reject(reason));} else {resolve(result);};});});
then函数的逻辑要考虑两种情况,一种是resolve函数先于then函数执行,这种情况发生在excutor的resolve语句为同步代码:
const p = new MyPromise((resolve,reject)=>resolve('result'));
当executor的resolve包含异步操作时,then函数是先于resolve执行的
const p = new MyPromise((resolve,reject)=>{setTimeout(()=>{resolve('result');},200)});
针对第一种情况直接then函数中直接能获取到resolve/reject的value/reason,然后将其传入onResolve/onReject执行即可。对于第二种情况,要先将onResolve/onReject保存起来,等到resolve/reject获取到value/reason的时候再执行。
第二种情况下对于then函数返回的Promsie也要特殊处理,then函数返回Promise是依赖于父级Promise的state和value/reason的,具体的处理方法是直接返回一个新的Promise,把它的resolve和reject方法通过闭包保存下来,等到resolve的时候执行。
onResolveCllbacks和onRejectCllbacks之所以是个数组是因为then函数可以被多次调用,会产生多个回调函数
var p = new MyPromise((resolve, reject) => {setTimeout(() => {resolve(1);}, 2000);});p.then(res=>console.log('第一次调用',res));p.then(res=>console.log('第二次调用',res));p.then(res=>console.log('第三次调用',res));
const p = new MyPromise((resolve, reject) => {setTimeout(() => {resolve(1);}, 100);});function createPromise(initialValue, value) {return new MyPromise((resolve, reject) => {setTimeout(() => {resolve(initialValue + value)}, 200);})};p.then(res=>createPromise(res,2)).then(res=>createPromise(res,3)).then(res=>console.log(res)); // 6
// 同步操作var p1 = new MyPromise((resolve, reject) => {resolve(1);});p1.then(res=>console.log(res)); //1// 异步操作var p2 = new MyPromise((resolve, reject) => {setTimeout(()=> resolve(1);,100)});p2.then(res=>console.log(res)); //1
var p = new MyPromise((resolve, reject) => {setTimeout(() => {resolve(1);}, 100);});p.then(res=>console.log('第一次调用',res)); //第一次调用 1p.then(res=>console.log('第二次调用',res)); //第二次调用 1p.then(res=>console.log('第三次调用',res)); //第三次调用 1
catch函数是onReject的语法糖,onReject与catch同时存在时只会执行onReject。catch函数也返回一个Promise,只不过这个场景很少用,下面的代码中不包含返回Promsie的部分。
MyPromise.prototype.catch = function (onReject) {if (this.state === 'reject') {onReject?.(this.reason)} else {typeof onReject === 'function' && this.onRejectCllbacks.push(onReject);};};
MyPromise.resolve = function (value) {return new MyPromise((resolve, reject) => resolve(value))};MyPromise.reject = function (reason) {return new MyPromise((resolve, reject) => reject(reason));};//使用const p1 = MyPromise.resolve('result');p1.then(res => console.log(res)); //resultconst p2 = MyPromise.reject('reason');p2.then(null, reason => console.log(reason)); //reason
Promise.all用来执行多个Promise,返回一个新的Promise,返回的这个promise规则如下:
Promise起到的作用是按顺序执行Promise,将它们的结果序列化。
MyPromise.all = function (fns) {const res = [];let count = 0;return new MyPromise((resolve, reject) => {function next() {fns[count].then(res => {if (count === fns.length) {resolve(res);} else {count++res.push(res);};}, reason => {reject(reason);});};next();});}; //使用var a = new MyPromise((resolve,reject)=>setTimeout(() => {resolve(1)}, 200));var b = new MyPromise((resolve,reject)=>setTimeout(() => {resolve(2);}, 100));var c = new MyPromise((resolve,reject)=>setTimeout(() => {resolve(3)}, 300));var p = Promise.all([a,b,c]);p.then(res=>console.log(res)); //[1,2,3]
race函数接收一个promsie数组进行赛马,返回一个新的promsie,并且这个promsie的结果依赖于先跑完的promise。
MyPromise.race = function (promiseFns) {return new MyPromise((resolve, reject) => {promiseFns.forEach((fn) => {fn.then(res => resolve(res), reason => reject(reason));});})};// 使用var a = new MyPromise((resolve,reject)=>setTimeout(() => {resolve(1)}, 500));var b = new MyPromise((resolve,reject)=>setTimeout(() => {resolve(2);}, 800));var c = new MyPromise((resolve,reject)=>setTimeout(() => {resolve(3)}, 300));var p = Promise.race([a,b,c]);p.then(res=>console.log(res),reason=>console.log(reason)); //3
使用compose串行promsie
function add1(x) {return x + 1;};function add3(x) {return x + 3;};function add5(x) {return x + 5;};const fns = [add1,add3,add5];const add9 = fns.reduce((promise,cb)=>promise.then(cb),Promise.resolve(1));add9.then(res=>console.log(res)); //10//进一步封装const add = fns => x => fns.reduce((promsie, cb) => promsie.then(cb), Promise.resolve(x));const add9 = add([add1, add3, add5]);add9(10).then(res => console.log(res)); //19
var a = new Promise(()=>{}).then();console.log(a);var b = Promise.resolve().then(res=>new Promise(()=>{}));console.log(b);
promsie无法取消的问题可以通过添加方法手动实现,或者使用一些像cancelPromise这样的三方库,下面的代码利用Promsie.race简单实现了对promsie的中断
function cancelPromsie(promise) {Promise.race([promise, new Promise((resolve, reject) => { resolve('cancel') })]);};var p1 = new Promise((resolve, reject) => {setTimeout(() => {console.log('p1')resolve('p1');}, 1000);});cancelPromsie(p1);
Promise的取消本意上是停止掉不需要的异步函数,取消promsie本身没有太大的意义,es6之所以没有支持取消promsie是因为取消promise会使得then函数的链式调用和Promsie.all的执行逻辑更加复杂。