‘回调地狱’是每个前端工程师的噩梦,promise/A+的出现让我们如梦初醒,原来异步编程还可以这样实现,promise让每一位前端工程师爱不释手。它规范化了前端异步编程,因此也成为了前端面试者躲不开的话题。今天我就把promise的源码实现分享给大家,供学习、参考。
Promise
console.log('-------------------promise源码解析----------------------')
/**
* 1. Promise 是一个类 exectuor 是一个立即执行函数
* 2. 有三个状态 默认是【PENDING】 FULFILED (成功态)、REJECTED(失败态),状态一经改变不能再修改
* 3. resolve和reject的结果传入到then中的回调函数中
* 4. 发布订阅模式实现异步执行
* 5. 如果promise返回一个普通值(不论是 resolve 还是reject中返回的)都传递到下一个then的成功中
* 6. 如果返回一个错误 一定走到下一次的失败
* 7. 如果返回的是一个promise 会采用promise的状态决定下一次的成功还是失败,如果离自己最近的then没有错误处理,会向下找。
* 8. 每次执行promise.then 都会返回一个'全新的promise'
*/
// 中间态 未决态
const PENDING = 'PENDING'
// 成功态
const FULFILED = 'FULFILED'
// 失败态
const REJECTED = 'REJECTED'
const resolvePromise = (promise2, x, resolve, reject) => {
// 循环引用 抛出异常
if (promise2 === x) {
return reject(new TypeError('TypeError: Chaining cycle detected for promise #<Promise>'))
}
// 防止第三方库状态会来回改变,调用多次
let called;
// 这里要进行严格的判断,因为这里还有兼容别人写的第三方的库实现的promise
// 1. 先判断是否是一个promise
if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
// 2.还要有then方法
/*
Object.defineProperty(x,'then',{
get(){
throw new Error()
}
})
*/
try {
let then = x.then
if (typeof then === 'function') {
// 走到这里就认为是prommise
// 不要写出x.then(),容易报错
then.call(x, y => {
// 成功
if (called) return;
called = true;
resolvePromise(promise2, y, resolve, reject)
}, e => {
// 失败
if (called) return;
called = true;
reject(e)
})
} else {
// 可能是对象
resolve(x)
}
} catch (error) {
if (called) return;
called = true;
reject(error)
}
} else {
resolve(x)
}
}
class MyPromise {
constructor(executor) {
this.status = PENDING;
this.value = null;
this.reason = null;
// 发布订阅模式
this.onFulfiledcallback = [];
this.onRejectedcallbalck = [];
let resolve = (value) => {
if (this.status === PENDING) {
// 一旦成功,状态不能再改变
this.status = FULFILED
this.value = value
this.onFulfiledcallback.forEach(fn => fn())
}
}
let reject = (reason) => {
if (this.status === PENDING) {
// 一旦失败,状态不再改变
this.status = REJECTED;
this.reason = reason;
this.onRejectedcallbalck.forEach(fn => fn())
}
}
try {
executor(resolve, reject)
} catch (error) {
// 如果异常,立即结束
console.log('error', 'inner')
reject(error)
}
}
then(onFulfailed, onRejected) {
onFulfailed = typeof onFulfailed === 'function' ? onFulfailed : v => v;
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
// then的链式调用的处理规范
const promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULFILED) {
// 返回的x如果是一个普通值没有问题
// 1. 如果返回的是一个promise 怎么办???
// 2. 如果返回一个错误怎么办???/ ====> 会被executor的异常蒱货
// 3.此时的promise2还没有创建完毕,会报错
// 4.使用事件循环机制的宏任务来解决
// let x = onFulfailed(this.value)
// console.log('x', x)
// resolvePromise(promise2, x, resolve, reject)
setTimeout(() => {
// 5. 使用setTimeout之后,抛出的异常外面的executor的trycatch无法再蒱货到
// 6. 需要在这里再次trycatch
try {
let x = onFulfailed(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
}
if (this.status === REJECTED) {
// let x = onRejected(this.reason)
// console.log('x', x)
// 返回的x如果是一个普通值没有问题
// 1. 如果返回的是一个promise 怎么办???
// 2. 如果返回一个错误怎么办???/
// resolvePromise(promise2, x, resolve, reject)
setTimeout(() => {
try {
let x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0);
}
if (this.status === PENDING) {
// 已下方式是可以的,有可能我们还会处理其他逻辑,
// 所以我们会使用AOP的思想来进行编程(面向切片编程)
// this.onFulfiledcallback.push(onFulfailed)
// this.onRejectedcallbalck.push(onRejected)
this.onFulfiledcallback.push(() => {
// todo other
// let x = onFulfailed(this.value)
// 返回的x如果是一个普通值没有问题
// 1. 如果返回的是一个promise 怎么办???
// 2. 如果返回一个错误怎么办???/
// resolvePromise(promise2, x, resolve, reject)
setTimeout(() => {
try {
let x = onFulfailed(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
})
this.onRejectedcallbalck.push(() => {
// todo other
// let x = onRejected(this.reason)
// 返回的x如果是一个普通值没有问题
// 1. 如果返回的是一个promise 怎么办???
// 2. 如果返回一个错误怎么办???/
// resolvePromise(promise2, x, resolve, reject)
setTimeout(() => {
try {
let x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0);
})
}
})
return promise2
}
catch(errCallback) {
// 实质是没有成功参数的then方法
return this.then(null, errCallback)
}
}
// Promise/A+ 规范 测试
MyPromise.deferred = function () {
const dfd = {};
dfd.promise = new MyPromise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
})
return dfd;
}
// npm install promises-aplus-tests -g
// promises-aplus-tests 16.手写promise.js
module.exports = MyPromise
Promise.all(面试的机率 *****)
/* Promise.all 1.全部完成才叫完成 2.返回的还是一个promise */
/*
Promise.all源码解析
*/
Promise.all = function (promises) {
return new Promise((resolve, reject) => {
const arr = []
// 解决异步并发的问题
let i = 0;
let processData = (index, data) => {
arr[index] = data
if (++i === promises.length) {
resolve(arr)
}
}
for (let i = 0; i < promises.length; i++) {
let currentPromise = promises[i]
if (isPromise(currentPromise)) {
currentPromise.then(data => {
processData(i, data)
}, reject)
} else {
processData(i, currentPromise)
}
}
})
}
/**
* 判断是否promise的方法
* @param {*} promise
*/
const isPromise = promise => {
if ((typeof promise === 'object' && promise !== 'null') || typeof promise === 'function') {
return typeof promise.then === 'function'
} else {
return false
}
}
Promise.race
/** Promise.race源码解析
* 1. 竞争机制
* 2.返回一个promise
*/
Promise.race = function (promises) {
return new Promise((resolve, reject) => {
for(let i = 0, len = promises.length; i < len; i++) {
let currentPromise = promises[i];
if(isPromise(currentPromise)){
currentPromise.then(resolve,reject)
}else{
resolve(currentPromise)
}
}
})
}
/*
1. 是对象或者函数
2. 具有then方法
*/
const isPromise = (promise) =>{
if((typeof promise === 'object' && promise !== null) || typeof promise === 'function'){
return typeof promise.then === 'function'
}else{
return false
}
}
Promise.resolve
/**
* 静态方法Promise.resolve源码解析
* 1.返回一个Promise
* 2.具有等待效果
*/
Promise.resolve = function (data) {
return new Promise((resolve, reject) => {
resolve(data)
})
}
Promise.reject
/**
* 静态方法Promise.reject源码解析
* 1.返回一个Promise
* 2.不具有等待效果
*/
Promise.reject = function (reason) {
return new Promise((resolve, reject) => {
reject(reason)
})
}