【前言】
对于Promise这块几乎是前端面试必问的模块了。《你不知道的JS》里对Promise的总结感觉还没有阮一峰《ES6 标准入门(第3版)》总结的好,建议大家可以看阮一峰的总结。
Promise是为了解决“回调地狱”的问题的。Promise简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
本文目录:
- Promise的特点
- Promise.prototype.then
- Promise.protype.catch
- Promise.protype.finally
- Promise.all
- Promise.race
- Promise.allSettled
- Promise.resolve
- Promise.reject
- 自己实现Promise对象
Promise 的特点:
- 无法取消,
- 包含resolve,pending,reject3个状态
- 通过构造器(new Promise)的形式会直接执行参数里的异步操作。
Promise.prototype.then
接受2个参数,第一个参数是处理“成功”的函数,第二个参数是处理“失败”的函数,返回一个Promise对象(既可以链式调用接着处理)
let p1 = new Promise((resolve, reject) => {
resolve('success')
})
p1.then((v) => {
console.log(v)
return Promise.resolve(123)//给下一个then的函数提供入参
},
(err) => {
console.log(err)
}
).then((v) => {
console.log(v)
})
Promise,protype.catch
这个方法是用于捕获错误的,和promise.prototype.then第二个函数捕获错误的区别是这样写更加简洁清晰(注意:如果之前没有捕获过,catch和then都能捕获之前第一个发生错误的地方)
如果之前没发生错误可以通过then进行链式调用:then().then()... :
let p1 = new Promise((resovle, reject) => {
resovle('success1')
// reject('err1');
}).then((v) => {
console.log('v1', v)
return new Promise((resolve, reject) => {
resolve('success2')
})
}, (err) => {
}).then((v) => {
console.log('v2', v)//如果;之前没发生错误可以通过then进行链式调用then().then()
},(err) => {
})
//v1 success1
//v2 success2
中途发生错误,如果是紧挨着的上个promise中发生错误,那么可以通过then的第二个参数函数捕获也可以通过catch捕获,但是catch写起来更加清晰:
let p1 = new Promise((resovle, reject) => {
reject('err1');
}).then((v) => {
return new Promise( (resovle, reject) => {
reject('err2')
})
}).then(v => {
}, err => {
console.log('err', err)
})
//等同于:
let p1 = new Promise((resovle, reject) => {
reject('err1');
}).then((v) => {
return new Promise( (resovle, reject) => {
reject('err2')
})
}).catch((err) => {
console.log('err', err)
})
promise.protype.finally
简单一句话就是Promise不管之前什么情况,最后都会执行的地方。
let p1 = new Promise((resovle, reject) => {
reject('err1');
}).then((v) => {
return new Promise( (resovle, reject) => {
reject('err2')
})
}).catch((err) => {
console.log('err', err)
}).finally(()=> {
//.....
})
promise.all
const p1 = new Promise((resolve, reject) => {
resolve('p1')
})
const p2 = new Promise((resolve, reject) => {
// resolve('p2')
reject('p2')
})
const p3 = new Promise((resolve, reject) => {
resolve('p3')
})
Promise.all([p1,p2,p3]).then(([v1, v2, v3] = v) => {
// const [v1, v2, v3] = v;
console.log(v1, v2, v3)
}).catch((err) => {
console.log('err', err)
} )
以上面代码为例,
- Promise.all 接受一个具有Iterator接口的对象作为参数。
- 如果元素不是promise对象则会通过Promise.resolve转化成Promise对象,
3, 返回一个新的promise对象. - 只有当p1, p2, p3都resolve才会进入then的第一个回调函数里,参数是所有resove的值组成的数组。否则第一个发生的错误被catch捕获(或者then第二个参数捕获,无论哪种捕获前提是p1,p2,p3自身没有去捕获错误)。
Promise.race
和Promise.all一样,不同点是:
- 第一个值是resolve的,则会进入then的第一个回调函数里。否则被catch捕获
其中一个用途就是设置Promise超时时间:
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('p1')
}, 1000)
// resolve('p1')
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('p2')
}, 2000)
// resolve('p2')
// reject('p2')
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('p3')
}, 3000)
// resolve('p3')
})
const myTimeOut = new Promise((resolve, reject) => {
setTimeout(() => {
reject('超时了')
}, 500)
})
Promise.race([p1,p2,p3, myTimeOut]).then((v) => {
console.log(v)
// console.log('rlt', v1, v2, v3)
}).catch((err) => {
console.log('err', err)
} )
Promise.allSettled
这个API主要是你不all的不足的,和all一样,区别是:
- 其结果总是fullfiled
- 无论每个promise返回的结果如何,allSettled 都会等待所有结果返回,再进入then执行(想设置超时则给具体的promise设置),其结果是一个包含直接执行结果的数组:
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('p1')
}, 1000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('p2')
}, 2000)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('p3')
}, 3000)
// resolve('p3')
})
Promise.allSettled([p1,p2,p3]).then((v) => {
console.log(v)
}).catch((err) => {
console.log('err', err)
} )
// [
// { status: 'fulfilled', value: 'p1' },
// { status: 'fulfilled', value: 'p2' },
// { status: 'rejected', reason: 'p3' }
// ]
Promise.resolve
该方法接受一个参数,返回一个promised对象。根据传入参数的不同,有如下是区别:
- 如果传入的是个一promise对象,则原封不动的返回传入的promise对象。
- 如果传入的是"thenable"——即含有then方法的对象。则会将该对象转化为Promsie对象并且立即执行then方法
- 如果传入的是不是“thenable”或者不是对象,则会返回一个新的Promise对象,状态为fullfileld,并且在then里可以获取传入的参数。(可以用于转化成promise对象)
- 如果为传入任何值,则生成一个新的Promise对象 ,状态为fullfileld。
Promise.reject
其他和Promise.resolve一样,不同点是reject的参数会看原封不动的作为后面错误捕获的理由.
自己实现Promise对象
Promise有很多规范,我们按照其规范在结合文章开头阮一峰对Promise的总结一步一步实现即可,ES6用的是Promises/A+规范
Promise 有四个基础特性,所有API都可以在这四个基础特性上完善:
- 构造函数的实现,就是通过new 创建一个Promise对象
- Promise.prototype.then的实现
- resolve的实现
- reject的实现
我们先逐步实现上述4个特性,最后会贴上所有API的完整代码:
理下构造函数大致要做的7件事情:
- 定义resolve方法
- 定义reject方法
- 定下promise的状态
- 定义下promise的回调函数集(包括fullfilled和rejected)
- 执行传入的那个函数参数
- 非promise类型转promise类型
- 一些错误处理
let myPromise = (function () {
stateArr = ['fullfilled', 'rejected', 'pending'];//全局中定义下状态集合,方便取用
function Promise(resolver) {//构造函数
let self = this;
self.state = 'pending';// 初始化状态值
self.callbacks = [];//存放resolve函数集合和reject函数集合,比如同一个promise多次调用then就会出现累加的情况
if (typeof resolver !== 'function') {// 构造函数传入的resolver必须是函数
throw new TypeError(`Promise resolver ${resolver} is not a function`);
} else {
if (!!!(this instanceof Promise)) {//不是promise类型的转化为promise类型
console.log('创建Promise成功')
return new Promise(resolver)
}
}
function resolve(value) {
setTimeout(() => {
if (self.state !== stateArr[2]) {//一旦状态改变,就不会再变
return
} else {
//改变状态
self.state = stateArr[0];
//存储resolve的值
self.data = value;
self.callbacks.forEach((item, index, arr) => {
try {
item.onResolved(value);
} catch (e) {
reject(e)
}
})
}
})
}
function reject(reason) {
setTimeout(() => {
if (self.state !== stateArr[2]) {//一旦状态改变,就不会再变
return
} else {
//改变状态
self.state = stateArr[1];
self.data = reason;
self.callbacks.forEach((item, index, arr) => {
try {
item.onRjected(reason);
} catch (e) {
reject(e)
}
})
}
})
}
try {
resolver(resolve, reject);
} catch (e) {
reject(e)
}
}
})()
理下Promise.prototype.then要做的几件事情
- 返回新的promise
- 将onResolved和onRrejected添加到回调函数集里
- 处理返回同一个promise的情况
- 处理返回“thenable”的情况
function resolvePromise(promise, x, resolve, reject) {//处理resolve的函数
let thenCalledOrThrow = false;
if (promise === x){ //不能返回同一个promise
throw reject(new TypeError(`Chaining cycle detected for promise!`))
} else {
if ((x !== null) && typeof x === 'object' || typeof x === 'function') {//处理thenable
try {
let then = x.then;
if (typeof then === 'function') {
then.call(x, function rs(rlt) {
if (thenCalledOrThrow) {
return
} else {
thenCalledOrThrow = true;
resolvePromise(promise, rlt, resolve, reject)
}
}, function rj(reason) {
if (thenCalledOrThrow) {
return
} else {
thenCalledOrThrow = true;
reject(reason)
}
})
} else {
resolve(x)
}
}catch(e) {
if (thenCalledOrThrow) {
return
} else {
thenCalledOrThrow = true;
reject(e)
}
}
} else {
resolve(x)
}
}
}
Promise.prototype.then = function (onResolved, onRjected) {
let self = this;
let promise2;
onResolved = typeof onResolved === 'function' ? onResolved : function(v) {return v};
onRjected = typeof onRjected === 'function' ? onRjected : function(v) {throw v};
if (self.state === stateArr[0]) {//同一个promise重复调用then就会出现这种情况,而不同的情况会造成要处理的self.data值不一样,所以会多出个resolvePromise函数来统一处理
return promise2 = new myPromise((resolve, reject) => {
setTimeout(() => {////这里用异步的原因就是让then将所有的事做完后(回调函数已经放到回调函数集合的数组里了),再进行resolve。
let x = onResolved(self.data);
resolvePromise(promise2, x, resolve, reject)
})
})
} else {
if (self.state === stateArr[1]) {//同一个promise重复调用then就会出现这种情况
return promise2 = new myPromise((resolve, reject) => {
setTimeout(() => {//这里用异步的原因就是让then将所有的事做完后(回调函数已经放到回调函数集合的数组里了),再进行resolve。
let x = onRjected(self.data);
resolvePromise(promise2, x, resolve, reject)
})
})
} else {
return promise2 = new myPromise((resolve, reject) => {
self.callbacks.push({//这里不用异步的原因就是因为这是放到回到函数集里执行,那里已经是异步了
onResolved: function (value) {
try {
let x = onResolved(value);
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
},
onRjected: function (reason) {
try {
let x = onRjected(reason);
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}
})
})
}
}
}
}
上诉4个特性的完整代码:
var myPromise = (function () {
stateArr = ['fullfilled', 'rejected', 'pending'];//全局中定义下状态集合,方便取用
//构造函数
function Promise(resolver) {//构造函数
let self = this;
self.state = 'pending';// 初始化状态值
self.callbacks = [];//存放resolve函数集合和reject函数集合,比如同一个promise多次调用then就会出现累加的情况
if (typeof resolver !== 'function') {// 构造函数传入的resolver必须是函数
throw new TypeError(`Promise resolver ${resolver} is not a function`);
} else {
if (!!!(this instanceof Promise)) {//不是promise类型的转化为promise类型
console.log('创建Promise成功')
return new Promise(resolver)
}
}
function resolve(value) {
setTimeout(() => {//这里用异步的原因就是让then将所有的事做完后(回调函数已经放到回调函数集合的数组里了),再进行resolve。
if (self.state !== stateArr[2]) {//一旦状态改变,就不会再变
return
} else {
//改变状态
self.state = stateArr[0];
//存储resolve的值
self.data = value;
self.callbacks.forEach((item, index, arr) => {
try {
item.onResolved(value);
}catch(e) {
reject(e)
}
})
}
})
}
function reject(reason) {
setTimeout(() => {//这里用异步的原因就是让then将所有的事做完后(回调函数已经放到回调函数集合的数组里了),再进行resolve。
if (self.state !== stateArr[2]) {//一旦状态改变,就不会再变
return
} else {
//改变状态
self.state = stateArr[1];
self.data = reason;
self.callbacks.forEach((item, index, arr) => {
try {
item.onRjected(reason);
} catch (e) {
reject(e)
}
})
}
})
}
try {
resolver(resolve, reject);
} catch (e) {
reject(e)
}
}
//promise 结果处理
function resolvePromise(promise, x, resolve, reject) {//处理resolve的函数
let thenCalledOrThrow = false;
if (promise === x){ //不能返回同一个promise
throw reject(new TypeError(`Chaining cycle detected for promise!`))
} else {
if ((x !== null) && typeof x === 'object' || typeof x === 'function') {//处理thenable
try {
let then = x.then;
if (typeof then === 'function') {
then.call(x, function rs(rlt) {
if (thenCalledOrThrow) {
return
} else {
thenCalledOrThrow = true;
resolvePromise(promise, rlt, resolve, reject)
}
}, function rj(reason) {
if (thenCalledOrThrow) {
return
} else {
thenCalledOrThrow = true;
reject(reason)
}
})
} else {
return resolve(x)
}
}catch(e) {
if (thenCalledOrThrow) {
return
} else {
thenCalledOrThrow = true;
reject(e)
}
}
} else {
resolve(x)
}
}
}
//promise then方法
Promise.prototype.then = function (onResolved, onRjected) {
let self = this;
let promise2;
onResolved = typeof onResolved === 'function' ? onResolved : function(v) {return v};
onRjected = typeof onRjected === 'function' ? onRjected : function(v) {throw v};
if (self.state === stateArr[0]) {//同一个promise重复调用then就会出现这种情况,而不同的情况会造成要处理的self.data值不一样,所以会多出个resolvePromise函数来统一处理
return promise2 = new myPromise((resolve, reject) => {
setTimeout(() => {////这里用异步的原因就是让then将所有的事做完后(回调函数已经放到回调函数集合的数组里了),再进行resolve。
let x = onResolved(self.data);
resolvePromise(promise2, x, resolve, reject)
})
})
} else {
if (self.state === stateArr[1]) {//同一个promise重复调用then就会出现这种情况
return promise2 = new myPromise((resolve, reject) => {
setTimeout(() => {//这里用异步的原因就是让then将所有的事做完后(回调函数已经放到回调函数集合的数组里了),再进行resolve。
let x = onRjected(self.data);
resolvePromise(promise2, x, resolve, reject)
})
})
} else {
return promise2 = new myPromise((resolve, reject) => {
self.callbacks.push({//这里不用异步的原因就是因为这是放到回到函数集里执行,那里已经是异步了
onResolved: function (value) {
try {
let x = onResolved(value);
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
},
onRjected: function (reason) {
try {
let x = onRjected(reason);
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}
})
})
}
}
}
return Promise;
})();
其他API完善:
var myPromise = (function () {
stateArr = ['fullfilled', 'rejected', 'pending'];//全局中定义下状态集合,方便取用
//构造函数
function Promise(resolver) {//构造函数
let self = this;
self.state = 'pending';// 初始化状态值
self.callbacks = [];//存放resolve函数集合和reject函数集合,比如同一个promise多次调用then就会出现累加的情况
if (typeof resolver !== 'function') {// 构造函数传入的resolver必须是函数
throw new TypeError(`Promise resolver ${resolver} is not a function`);
} else {
if (!!!(this instanceof Promise)) {//不是promise类型的转化为promise类型
console.log('创建Promise成功')
return new Promise(resolver)
}
}
function resolve(value) {
asyncFn(() => {//这里用异步的原因就是让then将所有的事做完后(回调函数已经放到回调函数集合的数组里了),再进行resolve。
if (self.state !== stateArr[2]) {//一旦状态改变,就不会再变
return
} else {
//改变状态
self.state = stateArr[0];
//存储resolve的值
self.data = value;
self.callbacks.forEach((item, index, arr) => {
try {
item.onResolved(value);
}catch(e) {
reject(e)
}
})
}
})
}
function reject(reason) {
asyncFn(() => {//这里用异步的原因就是让then将所有的事做完后(回调函数已经放到回调函数集合的数组里了),再进行resolve。
if (self.state !== stateArr[2]) {//一旦状态改变,就不会再变
return
} else {
//改变状态
self.state = stateArr[1];
self.data = reason;
self.callbacks.forEach((item, index, arr) => {
try {
item.onRjected(reason);
} catch (e) {
reject(e)
}
})
}
})
}
try {
resolver(resolve, reject);
} catch (e) {
reject(e)
}
}
//promise 结果处理
function resolvePromise(promise, x, resolve, reject) {//处理resolve的函数
let thenCalledOrThrow = false;
if (promise === x){ //不能返回同一个promise
throw reject(new TypeError(`Chaining cycle detected for promise!`))
} else {
if ((x !== null) && typeof x === 'object' || typeof x === 'function') {//处理thenable
try {
let then = x.then;
if (typeof then === 'function') {
then.call(x, function rs(rlt) {
// if (thenCalledOrThrow) {
// return
// } else {
// thenCalledOrThrow = true;
resolvePromise(promise, rlt, resolve, reject)
// }
}, function rj(reason) {
// if (thenCalledOrThrow) {
// return
// } else {
// thenCalledOrThrow = true;
reject(reason)
// }
})
} else {
return resolve(x)
}
}catch(e) {
// if (thenCalledOrThrow) {
// return
// } else {
// thenCalledOrThrow = true;
reject(e)
// }
}
} else {
resolve(x)
}
}
}
//promise then方法
Promise.prototype.then = function (onResolved, onRjected) {
let self = this;
let promise2;
onResolved = typeof onResolved === 'function' ? onResolved : function(v) {return v};
onRjected = typeof onRjected === 'function' ? onRjected : function(v) {throw v};
if (self.state === stateArr[0]) {//同一个promise重复调用then就会出现这种情况,而不同的情况会造成要处理的self.data值不一样,所以会多出个resolvePromise函数来统一处理
return promise2 = new myPromise((resolve, reject) => {
asyncFn(() => {////这里用异步的原因就是让then将所有的事做完后(回调函数已经放到回调函数集合的数组里了),再进行resolve。
let x = onResolved(self.data);
resolvePromise(promise2, x, resolve, reject)
})
})
} else {
if (self.state === stateArr[1]) {//同一个promise重复调用then就会出现这种情况
return promise2 = new myPromise((resolve, reject) => {
asyncFn(() => {//这里用异步的原因就是让then将所有的事做完后(回调函数已经放到回调函数集合的数组里了),再进行resolve。
let x = onRjected(self.data);
resolvePromise(promise2, x, resolve, reject)
})
})
} else {
return promise2 = new myPromise((resolve, reject) => {
self.callbacks.push({//这里不用异步的原因就是因为这是放到回到函数集里执行,那里已经是异步了
onResolved: function (value) {
try {
let x = onResolved(value);
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
},
onRjected: function (reason) {
try {
let x = onRjected(reason);
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}
})
})
}
}
}
//由then实现catch
Promise.prototype.catch = function(onRjected) {
return this.then(null, onRjected)
}
//finally实现
Promise.prototype.finally = function(fn) {
return this.then((v) => {
// setTimeout(fn);
fn();
return v;
}, () => {
// setTimeout(fn);
fn();
throw v
})
}
//Promise.resolve的实现
Promise.resolve = function(v) {
let promise2;
if(v instanceof Promise === true) {
return v
}else {
return promise2 = new Promise((resolve, reject) => {
resolvePromise(promise2, v, resolve, reject)
})
}
}
//Promise.reject的实现
Promise.reject = function(x) {
let promise = new Promise((resolve, reject) => {
reject(x)
})
return promise
}
//Promise.all的实现
Promise.all = function (iterator) {
let len = 0;
let count = 0;
let rltArr = [];
let index = 0;
return new Promise((resolve, reject) => {
for (let i of iterator) {
len ++;
index++;
(function(index) {
Promise.resolve(i).then(v => {
count++;
rltArr.push({
value: v,
index: index
});
if (count === len) {
resolve(sortObjArrToArr(rltArr))
}
}, e => {
reject(e)
})
})(index)
}
})
}
//Promise.race 的实现
Promise.race = function(iterator) {
return new Promise((resolve, reject) => {
for(let i of iterator) {
Promise.resolve(i).then(v => {
resolve(v)
}, e => {
reject(e)
})
}
})
}
//Promise.allSettled 的实现
Promise.allSettled = function(iterator) {
let rltArr = [];
let len = Object.keys(iterator).length;
let count = 0;
let createObj = (state, v, index) => {
let obj = {};
obj.status = state;
obj.value = v;
Object.defineProperty(obj, 'index', {
writable: true,
enumerable: false,
value: index,
configurable: false,
})
return obj
}
return new Promise((resolve, reject) => {
let arr = Object.keys(iterator);
arr.forEach((item, index, arr) => {
Promise.resolve(iterator[item]).then(v => {
count++;
rltArr.push(createObj(state[0], v, index))
rltArr.sort((a, b) => {
return a.index - b.index
})
if(len === count) {
resolve(rltArr)
}
}, e => {
count++;
rltArr.push(createObj(state[1], e, index))
rltArr.sort((a, b) => {
return a.index - b.index
})
if(len === count) {
resolve(rltArr)
}
})
})
})
}
return Promise;
})();
const p1 = new myPromise((resolve, reject) => {
setTimeout(() => {
resolve('p1')
},4000)
})
const p2 = new myPromise((resolve, reject) => {
setTimeout(() => {
reject('p2')
// resolve('p2')
},2000)
})
const p3 = new myPromise((resolve, reject) => {
setTimeout(() => {
// console.log('p3');
reject('p3')
},3000)
})
myPromise.allSettled([p1,p2,p3]).then((v) => {
console.log('vx', v)
})
function sortObjArrToArr(objArr) {
objArr.sort((a, b) => a.index - b.index);
return objArr.reduce(function(acc, currentValue, index, arr) {
acc.push(currentValue.value);
return acc
}, [])
}
//异步函数
function asyncFn(fn) {
//降级顺序: asyncFn(microTask, node) => mutationOberserver(microTask, browser) => MessageChannel(maskTask, browser) =>setImmediate => setTimeout
if(typeof process !== 'undefined' && typeof (process.nextTick) !== 'undefined') {
process.nextTick(fn)
}else if(typeof MutationObserver !== 'undefined'){
let count = 1;
const observer = new MutationObserver(fn);
const textNode = document.createTextNode(String(count));
observer.observe(textNode, {
characterData: true
});
count = (count + 1) % 2;
textNode.data = String(count);
}else if(typeof MessageChannel !== 'undefined') {
const channel = new MessageChannel();
channel.port1.onmessage = fn;
channel.port2.postMessage(0);
}else if(typeof setImmediate !== 'undefined') {
setImmediate(fn)
}else {
setTimeout(fn)
}
}
很多其他版本promise的API也是从上面所述4个基础特性(构造函数,then,resolve,reject)演变出来的,大家可以自行扩展,有问题欢迎留言探讨!
网友评论