写在开头
- 这几天看到有些技术群在发
Promise
相关的一些实现,自己最近也在看ES6
的一些内容,于是打算自己也整理一下,提升一下理解; - 本文适合一些了解并使用过
Promise
的人,如果你没有了解或使用过Promise
,建议先看一下 阮一峰 ECMAScript6 入门 之Promise。
什么是Promise
- 异步编程的一种解决方案;
Promise
是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果;
特点
- 对象的状态不受外界影响。
Promise
对象代表一个异步操作,有三种状态:pending
(进行中)、fulfilled
(已成功)和rejected
(已失败)。 - 一旦状态改变,就不会再变,任何时候都可以得到这个结果。
Promise
对象的状态改变,只有两种可能:从pending
变为fulfilled
和从pending
变为rejected
。
简单实现
流程分析
- 图片来源 MDN
初始化一个Promise
- 原始的
Promise
// Promise构造函数接收一个executor函数,executor函数执行完同步或异步操作后,调用它的两个参数resolve和reject
const promise = new Promise(function(resolve, reject) {
/*
如果操作成功,调用resolve并传入value
如果操作失败,调用reject并传入reason
*/
})
复制代码
- 包含当前的状态
- 当前的值
fulfilled
的回调函数数组rejected
的回调函数数组
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
const NewPromise = function(executor) {
const _this = this;
_this.status = PENDING; // 状态
_this.data = undefined; // 值
_this.onResolvedCallback = []; // fulfilled的回调函数数组
_this.onRejectedCallback = []; // rejected的回调函数数组
// 成功
function resolve(value) { ... }
// 失败
function reject(reason) { ... }
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
复制代码
完善resole
和reject
resole
是成功的调用,需要不状态修改为fulfilled
,然后当前的值设为传入的值,遍历执行onResolvedCallback
中的回调。
// 成功
function resolve(value) {
只有pending状态才能转换为fulfilled / rejected
setTimeout(function() { // 异步执行所有的回调函数
if (_this.status === PENDING) {
_this.status = FULFILLED;
_this.data = value;
_this.onResolvedCallback.forEach(callback => callback(value));
}
});
}
复制代码
reject
的实现和resolve
类似,只是状态修改为rejected
及执行onRejectedCallback
。
// 失败
function reject(reason) {
setTimeout(function() { // 异步执行所有的回调函数
if (_this.status === PENDING) {
_this.status = REJECTED;
_this.data = reason;
_this.onRejectedCallback.forEach(callback => callback(reason));
}
});
}
复制代码
- 现在已经实现了基本的
Promise
,你可以像使用Promise
一样使用它,但还没有添加其他的方法。
// 测试
const promise = new NewPromise(function(resolve, reject) {
console.log('ss', 11)
})
复制代码
then
方法
Promise
对象有一个then
方法,用来注册在这个Promise
状态确定后的回调,then
方法需要写在原型链上(为什么要写在原型上不清楚的可能需要补一下JavaScript
基础了)。在Promise/A
标准中,明确规定了then
要返回一个新的对象,所以在我们的实现中也返回一个新对象。
// then挂载到原型上
NewPromise.prototype.then = function(onResolved, onRejected) {
const _this = this;
if ( typeof onResolved !== 'function') {
onResolved = function(value) { return value }
}
if ( typeof onRejected !== 'function') {
onRejected = function(reason) { throw reason }
}
// 公共判断
const common = function (data, resolve, reject) {
// 考虑到有可能throw,我们将其包在try/catch块里
try {
let value = _this.status === FULFILLED
? onResolved(data)
: onRejected(data)
if( value instanceof Promise) {
value.then(resolve, reject)
}
resolve(value)
} catch (error) {
reject(error)
}
}
// 公共判断
const pendingCommon = function (data, flag, resolve, reject) {
// 考虑到有可能throw,我们将其包在try/catch块里
try {
let value = flag === FULFILLED
? onResolved(data)
: onRejected(data)
if( value instanceof Promise) {
value.then(resolve, reject)
}
resolve(value)
} catch (error) {
reject(error)
}
}
if (_this.status === PENDING) {
return new NewPromise(function(resolve, reject) {
_this.onResolvedCallback.push((value) => {
pendingCommon(value, FULFILLED, resolve, reject);
})
_this.onRejectedCallback.push((reason) => {
pendingCommon(reason, REJECTED, resolve, reject);
})
})
} else { // resolve / reject
return new NewPromise(function (resolve, reject) {
setTimeout(function () {
common(_this.data, resolve, reject)
})
})
}
}
复制代码
对resolve
方法再次完善
- 判断
rosolve
的值是否是Promise
,如果是Promise
继续执行.then
方法。
// 成功
function resolve(value) {
// value 如果是Promise继续执行.then
+ if (value instanceof Promise) {
+ return value.then(resolve, reject)
+ }
...
}
复制代码
catch
方法
- 捕获
Promise
的异常错误。
// catch方法
NewPromise.prototype.catch = function(onRejected) {
return this.then(null, onRejected)
}
复制代码
finally
方法
finally()
方法用于指定不管Promise
对象最后状态如何,都会执行的操作。
// finally
NewPromise.prototype.finally = function (fun) {
return this.then((value) => {
return NewPromise.resolve(fun()).then(() => value);
}, (err) => {
return NewPromise.resolve(fun()).then(() => {
throw err;
});
});
};
复制代码
resolve
方法
- 现有对象转为
Promise
对象,实例的状态为fulfilled
。
NewPromise.resolve = function(value) {
if (value instanceof Promise) return value;
if (value === null) return null;
// 判断如果是promise
if (typeof value === 'object' || typeof value === 'function') {
try {
// 判断是否有then方法
let then = value.then;
if (typeof then === 'function') {
return new NewPromise(then.call(value)); // 执行value方法
}
} catch (e) {
return new NewPromise( (resolve, reject) =>{
reject(e);
});
}
}
return new NewPromise( (resolve, reject) =>{
resolve(value);
});
}
复制代码
reject
方法
- 现有对象转为
Promise
对象,实例的状态为rejected
。实现和resolve
方法类似,最后return改为reject
。
NewPromise.reject = function(reason) {
...
return new NewPromise( (resolve, reject) =>{
reject(reason);
});
}
复制代码
all
方法
Promise.all
方法用于将多个 Promise 实例,包装成一个新的Promise
实例;方法的参数可以不是数组,但必须具有Iterator
接口,且返回的每个成员都是Promise
实例。
// all
NewPromise.all = function(promises) {
return new NewPromise((resolve, reject) => {
const result = []
// 判断参数属否是数组
promises = Array.isArray(promises) ? promises : []
const len = promises.length;
if(len === 0) resolve([]);
let count = 0
for (let i = 0; i < len; ++i) {
if(promises[i] instanceof Promise) {
promises[i].then(value => {
count ++
result[i] = value
if (count === len) resolve(result)
}, reject)
} else {
count ++
result[i] = promises[i]
if (count === len) resolve(result)
}
}
})
}
复制代码
race
方法
Promise.race()
方法同样是将多个Promise
实例,包装成一个新的Promise
实例。- 只要参数之中有一个实例率先改变状态,
Promise.race()
的状态就跟着改变。那个率先改变的Promise
实例的返回值,就传递给Promise.race()
的回调函数。
// race
NewPromise.race = function (promises) {
return new NewPromise((resolve, reject) => {
promises = Array.isArray(promises) ? promises : []
promises.forEach(promise => {
promise.then(resolve, reject)
})
})
}
复制代码
deferred
方法
- 返回一个有promise方法的对象。
// defered
NewPromise.deferred = function() {
const dfd = {}
dfd.promise = new NewPromise(function(resolve, reject) {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
复制代码
allSettled
方法
Promise.allSettled()
方法接受一组Promise
实例作为参数,包装成一个新的Promise
实例,只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束。返回一个包含所有结果的数组。
NewPromise.allSettled = function (promises) {
return new NewPromise((resolve, reject) => {
promises = Array.isArray(promises) ? promises : []
let len = promises.length;
const argslen = len;
if (len === 0) return resolve([]);
let args = Array.prototype.slice.call(promises);
function resolvePromise(index, value) {
if(typeof value === 'object') { // 传入的是否是object
const then = value.then;
if(typeof then === 'function'){
then.call(value, function(val) {
args[index] = { status: 'fulfilled', value: val};
if(--len === 0) {
resolve(args);
}
}, function(e) {
args[index] = { status: 'rejected', reason: e };
if(--len === 0) {
reject(args);
}
})
}
}
}
for(let i = 0; i < argslen; i++){
resolvePromise(i, args[i]);
}
})
}
复制代码
测试
基础测试
// 测试
const promise = new NewPromise(function(resolve, reject) {
console.log('ss', 11);
resolve(123);
})
复制代码
then
测试
promise.then(val => {
console.log('val', val)
})
复制代码
catch
测试
const promise = new NewPromise(function(resolve, reject) {
console.log('ss', 11);
reject('errr')
})
promise.catch(err => {
console.log('err', err)
})
复制代码
finally
测试
const resolved = NewPromise.resolve(1);
const rejected = NewPromise.reject(-1);
const resolved1 = NewPromise.resolve(17);
const p = NewPromise.all([resolved, resolved1, rejected]);
p.then((result) => {
console.log('result', result)
}).catch(err => {
console.log('err', err)
}).finally(() => {
console.log('finally')
})
复制代码
resolve
测试
const resolved = NewPromise.resolve(1);
resolved.then(val => {
console.log('resolved', val)
})
复制代码
reject
测试
const rejected = NewPromise.reject(-1);
rejected.catch(val => {
console.log('rejected', val)
})
复制代码
all
测试
const resolved = NewPromise.resolve(1);
const rejected = NewPromise.reject(-1);
const resolved1 = NewPromise.resolve(17);
const p = NewPromise.all([resolved, resolved1, rejected]);
p.then((result) => {
console.log('result', result)
}).catch(err => {
console.log('err', err)
})
复制代码
allSettled
测试
const resolved = NewPromise.resolve(1);
const rejected = NewPromise.reject(-1);
const resolved1 = NewPromise.resolve(17);
const p = NewPromise.allSettled([resolved, resolved1, rejected]);
p.then((result) => {
console.log('result', result)
})
复制代码
全部代码
/*
* @Author: detanx
* @Date: 2020-05-11 17:39:52
* @Last Modified by: detanx
* @Last Modified time: 2020-05-11 17:39:52
*/
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
const NewPromise = function(executor) {
const _this = this;
_this.status = PENDING;
_this.data = undefined;
_this.onResolvedCallback = [];
_this.onRejectedCallback = [];
// 成功
function resolve(value) {
if (value instanceof Promise) {
return value.then(resolve, reject)
}
setTimeout(function() { // 异步执行所有的回调函数
if (_this.status === PENDING) {
_this.status = FULFILLED;
_this.data = value;
_this.onResolvedCallback.forEach(callback => callback(value));
}
})
}
// 失败
function reject(reason) {
setTimeout(function() { // 异步执行所有的回调函数
if (_this.status === PENDING) {
_this.status = REJECTED;
_this.data = reason;
_this.onRejectedCallback.forEach(callback => callback(reason));
}
})
}
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
// then
NewPromise.prototype.then = function(onResolved, onRejected) {
const _this = this;
if ( typeof onResolved !== 'function') {
onResolved = function(value) { return value }
}
if ( typeof onRejected !== 'function') {
onRejected = function(reason) { throw reason }
}
// 公共判断
const common = function (data, resolve, reject) {
// 考虑到有可能throw,我们将其包在try/catch块里
try {
let value = _this.status === FULFILLED
? onResolved(data)
: onRejected(data)
if( value instanceof Promise) {
value.then(resolve, reject)
}
resolve(value)
} catch (error) {
reject(error)
}
}
// 公共判断
const pendingCommon = function (data, flag, resolve, reject) {
// 考虑到有可能throw,我们将其包在try/catch块里
try {
let value = flag === FULFILLED
? onResolved(data)
: onRejected(data)
if( value instanceof Promise) {
value.then(resolve, reject)
}
resolve(value)
} catch (error) {
reject(error)
}
}
if (_this.status === PENDING) {
return new NewPromise(function(resolve, reject) {
_this.onResolvedCallback.push((value) => {
pendingCommon(value, FULFILLED, resolve, reject);
})
_this.onRejectedCallback.push((reason) => {
pendingCommon(reason, REJECTED, resolve, reject);
})
})
} else { // resolve / reject
return new NewPromise(function (resolve, reject) {
setTimeout(function () {
common(_this.data, resolve, reject)
})
})
}
}
NewPromise.resolve = function(value) {
if (value instanceof Promise) return value;
if (value === null) return null;
// 判断如果是promise
if (typeof value === 'object' || typeof value === 'function') {
try {
// 判断是否有then方法
let then = value.then;
if (typeof then === 'function') {
return new NewPromise(then.call(value)); // 执行value方法
}
} catch (e) {
return new NewPromise( (resolve, reject) =>{
reject(e);
});
}
}
return new NewPromise( (resolve, reject) =>{
resolve(value);
});
}
NewPromise.reject = function(value) {
if (value instanceof Promise) return value;
if (value === null) return null;
// 判断如果是promise
if (typeof value === 'object' || typeof value === 'function') {
try {
// 判断是否有then方法
let then = value.then;
if (typeof then === 'function') {
return new NewPromise(then.call(value)); // 执行value方法
}
} catch (e) {
return new NewPromise( (resolve, reject) =>{
reject(e);
});
}
}
return new NewPromise( (resolve, reject) =>{
reject(value);
});
}
// catch方法
NewPromise.prototype.catch = function(onRejected) {
return this.then(null, onRejected)
}
// finally
NewPromise.prototype.finally = function (fun) {
return this.then((value) => {
return NewPromise.resolve(fun()).then(() => value);
}, (err) => {
return NewPromise.resolve(fun()).then(() => {
throw err;
});
});
};
// defered
NewPromise.deferred = function() {
const dfd = {}
dfd.promise = new NewPromise(function(resolve, reject) {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
// all
NewPromise.all = function(promises) {
return new NewPromise((resolve, reject) => {
const result = []
promises = Array.isArray(promises) ? promises : []
const len = promises.length;
if(len === 0) resolve([]);
let count = 0
for (let i = 0; i < len; ++i) {
if(promises[i] instanceof Promise) {
promises[i].then(value => {
count ++
result[i] = value
if (count === len) resolve(result)
}, reject)
} else {
result[i] = promises[i]
}
}
})
}
// race
NewPromise.race = function (promises) {
return new NewPromise((resolve, reject) => {
promises = Array.isArray(promises) ? promises : []
promises.forEach(promise => {
promise.then(resolve, reject)
})
})
}
// allSettled
NewPromise.allSettled = function (promises) {
return new NewPromise((resolve, reject) => {
promises = Array.isArray(promises) ? promises : []
let len = promises.length;
const argslen = len;
if (len === 0) return resolve([]);
let args = Array.prototype.slice.call(promises);
function resolvePromise(index, value) {
if(typeof value === 'object') {
const then = value.then;
if(typeof then === 'function'){
then.call(value, function(val) {
args[index] = { status: 'fulfilled', value: val};
if(--len === 0) {
resolve(args);
}
}, function(e) {
args[index] = { status: 'rejected', reason: e };
if(--len === 0) {
reject(args);
}
})
}
}
}
for(let i = 0; i < argslen; i++){
resolvePromise(i, args[i]);
}
})
}
// 测试
// const promise = new NewPromise(function(resolve, reject) {
// console.log('ss', 11)
// // resolve(123)
// reject('errr')
// // throw 'ree'
// })
// promise.catch(val => {
// console.log('val', val)
// })
// const promise1 = new NewPromise(function(resolve, reject) {
// console.log('ss', 11)
// resolve(123)
// // reject('errr')
// })
// const rejected = NewPromise.reject(-1);
// rejected.catch(val => {
// console.log('rejected', val)
// })
// console.log('resolved', resolved)
const resolved = NewPromise.resolve(1);
const rejected = NewPromise.reject(-1);
const resolved1 = NewPromise.resolve(17);
const p = NewPromise.all([resolved, resolved1, rejected]);
p.then((result) => {
console.log('result', result)
}).catch(err => {
console.log('err', err)
})
复制代码
参考
阮一峰 ECMAScript 6 入门
22 道高频 JavaScript 手写面试题及答案
- 有帮到您的话留下你的赞👍呗,你的支持就是我产出的动力。
补充
对Promise.all
存在疑
- 测试了原生的
Promise.all
方法。
- 当所有的fulfilled
修改all方法异步成功返回顺序
- 上面的all方法代码已更新。
- 测试用例,感谢 AshoneA 提出的问题。
const promise1 = new Promise(resolve => {
setTimeout(() => {
resolve("promise1");
}, 200);
});
const promise2 = new Promise(resolve => {
setTimeout(() => {
resolve("promise2");
}, 100);
});
NewPromise.all([promise1, promise2]).then(value => {
console.log(value);
});
Promise.all([promise1, promise2]).then(value => {
console.log(value);
});
复制代码
- 之前测试结果。[测试地址](
- 修改后测试结果。