手撕Promise,从入门到入坟
第一章 准备
1.1.区别实例对象与函数对象
实例对象: new 函数产生的对象,称为实例对象,简称为对象
**函数对象: **将函数作为对象使用时,简称为函数对象

1.2.二种类型的回调函数
1.2.1同步回调
1.理解: 立即执行, 完全执行完了才结束, 不会放入回调队列中
2.例子: 数组遍历相关的回调函数 / promise 的 excutor函数
1.2.2异步回调

1.3.JS 的error处理
1.3.1错误的类型
- Error: 所有错误的父类型
- ReferenceError: 引用的变量不存在
- TypeError: 数据类型不正确的错误
- RangeError: 数据值不在其所允许的范围内
- SyntaxError: 语法错误

第二章 promise的理解和使用
2.1为什么要用 Promise?
2.1.1.理解
1.抽象表达:
1) Promise是一门新的技术(ES6规范)
2) Promise是Js中进行异步编程的新解决方案
备注:旧方案是单纯使用回调函数
- 具体表达:
- 从语法上来说: Promise是一个构造函数
- 从功能上来说: promise对象用来封装一个异步操作并可以获取其成功/失败的结果值
2.1.2.promise的状态改变
-
pending变为resolved
-
pending 变为rejected
说明:只有这2种,且一个promise对象只能改变一次
无论变为成功还是失败,都会有一个结果数据
成功的结果数据一般称为value,失败的结果数据一般称为reason
2.1.3.promise的基本流程

2.1.4.promise的基本使用

2.2为什么要用Promise?
2.2.1. 指定回调函数的方式更加灵活
- 旧的: 必须在启动异步任务前指定
- promise: 启动异步任务 → 返回promie对象 → 给promise对象绑定回调函数(甚至可以在异步任务结束后指定/多个)
2.2.2.支持链式调用,可以解决回调地狱问题
1.什么是回调地狱?
- 回调函数嵌套调用,外部回调函数异步执行的结果是嵌套的回调执行的条件

2.回调地狱的缺点?
- 不便于阅读
- 不便于异常处理
3.解决方案?
- promise链式调用
4.终极解决方案?
- async/await


<body>
<script>
// 原生的异步任务调用(回调会直接执行)
function successCallback() { }
function failureCallback() { }
asyncAudioPlayer(options, successCallback, failureCallback);
// Promise: 需要的时候再调用
const promise = asyncAudioPlayer(options);
setTimeout(() => {
promise.then(successCallback, failureCallback);
}, 3000)
// 回调地狱: result1 -> doSomething1 -> result2 -> doSomething2 -> result3 -> doSomething3
doSomething1(function (result1) {
doSomething2(result1, function (result2) {
doSomething3(result2, function (result3) {
console.log(result3)
}, failureCallback)
}, failureCallback);
}, failureCallback)
// promise 使用promise的链式调用解决回调地狱
doSomething1() // 第一层的函数
.then(
function (result1) {
return doSomething2(result1) // 第二层的函数
}
).then(
function (result2) {
return doSomething3(result2) // 第三层的函数
}
).then(
function (result3) {
return console.log(result3)
}
).catch(failureCallback)
// 使用 async/await
async function asyncAudioPlayer() {
try {
let result1 = await doSomething1();
let result2 = await doSomething2(result1);
let result3 = await doSomething3(result2);
console.log(result3)
} catch (error) {
failureCallback
}
}
</script>
</body>
2.3如何使用Promise?
2.3.1.API
- Promise构造函数: Promise (executor){}
(1) executor函数: 执行器(resolve, reject)=> {}
(2)resolve函数: 内部定义成功时我们调用的函数value =>{}
(3) reject函数: 内部定义失败时我们调用的函数reason=>{}
说明: executor会在Promise 内部立即同步回调, 异步操作在执行器中执行
- Promise.prototype.then方法: (onResolved, onRejected) =>{}
<body>
<script>
// 直接写在执行器内部
new Promise((resolve, reject) => {
setTimeout(() => {
resolve("inner success data");
// reject(); // 状态只能改变一次
}, 0)
}).then(
value => console.log(value)
).catch(
reason => console.log(reason)
)
// Promise.resolve()
Promise.resolve("outer success data").then(
value => console.log(value)
).catch(
reason => console.log(reason)
)
// 产生一个成功值为1 的promise
const promise1 = new Promise((resolve, reject) => {
resolve(1)
})
const promise2 = Promise.resolve(2)
const promise3 = Promise.reject(3)
promise1.then(value => console.log(value));
promise2.then(value => console.log(value));
promise3.catch(reason => console.log(reason));
// Promise.all(value:list) : 所有成功后成功,只要一个失败就是失败
const pall = Promise.all([promise1, promise2, promise3])
pall.then(values => console.log(`onResolved return values:`, values));
pall.catch(reason => console.log(`onrejected return reason: `, reason)) // this
// Promise.race(value:list) :根据最先获取到的状态值,决定return (onrResolved \ onRejected)
const prace = Promise.race([promise1, promise2, promise3])
prace.then(value => console.log(`onResolved return value:`, value)); // this
prace.catch(reason => console.log(`onRejected return reason:`, reason));
</script>
</body>
2.3.2.promise 的几个关键问题
1.如何改变promise的状态?
(1) resolve(value):如果当前是pendding 就会变为resolved
(2) reject(reason):如果当前是pendding就会变为rejected
(3) 抛出异常:如果当前是pendding 就会变为rejected
<body>
<script>
const promise = new Promise((resolve, reject) => {
// resolve(1); // 确定promise的状态由: pending -> resolved
// reject(2); // 确定promise的状态由: pending -> rejected
throw new Error('2 this is a rejected promise through throw new Error')
})
promise.then(value => console.log(value));
promise.catch(reason => console.log(reason));
console.log(promise);
// promise status same call same status handler
promise.then(value => console.log(value));
promise.then(value => console.log(value));
promise.catch(reason => console.log(reason));
promise.catch(reason => console.log(reason));
</script>
</body>
2.一个promise指定多个成功/失败回调函数,都会调用吗?
当promise改变为对应状态时都会调用
.then() # 为同步执行 .then(callback) # callback 为异步执行
3.改变promise状态和指定回调函数谁先谁后?
(1)都有可能,正常情况下是先指定回调再改变状态,但也可以先改状态再指定回调
(2)如何先改状态再指定回调?
①在执行器中直接调用resolve()/reject()
②延迟更长时间才调用then()
(3) 什么时候才能得到数据?
Promise:
- then
- 串联多任务
- 异常穿透
- 中断promise链
1.先指定promise的状态,后获取数据时,再给定回调函数。
// 1.此中写法是:先指定promise的状态,后获取数据时,再给定回调函数。
setTimeout(() => {
(function () {
const promise = new Promise((resolve, reject) => {
// executor 范围是同步执行,而单独对:resolve\reject 函数来说,它是异步函数。
console.log("(1) Promise inner env, 由于executor是属于同步回调,所以先执行, (2) resolve添加到了async queue, 此时的异步队列: [resolve,]")
// resolve 表示的是成功的回调,中间的值表示返回的值,在执行器中直接书写,表示先指定promise的状态
resolve(1);
})
// 对于.then 是同步函数,而.then(callback) 中的callback是异步函数。
console.log("(3) Promise 同步回调 then, 发现callback 是异步回调,(4) 添加到 async queue中,此时的异步队列: [resolve,onResolved]")
promise.then(
value => { console.log("(6) Promise async return handler env"); console.log(value) }
) // 通过then()方法,延迟获取promise 返回的数据, 通过后面给定的回调函数对返回的数据进行处理。
console.log('(5) Promise outer env')
})();
});
2.先指定回调函数, 再指定Promise 状态的
// 2.此状态下的Promise 是先指定回调函数, 再指定Promise 状态的
setTimeout(() => {
(function () {
const promise = new Promise((resolve, reject) => {
console.log("(1) executor 是同步回调, 执行到setTimeout 时发现,setTimeout是一个定时器,开启一个异步任务,同时将resolve(1)添加到异步队列中, 此时异步任务队列中的任务时:[resolve(1),]")
setTimeout(() => { resolve(2); console.log("(5) setTimeout return Promise status pending => resolved") }, 200)
})
console.log("(2) .then 函数是同步回调, 执行到then(callback)中的callback时,发现callback是一个异步回调函数,将其添加到异步队列中:[resolve(1), value, reason]")
promise.then(
value => console.log('(4) Promise onResolved with value,发现promise 的状态一直是 pending 等待status的改变 (6)', value),
reason => console.log("(4) Promise onRejected with reason, 发现promise 的状态一直是 pending 等待status的改变 (6)", reason)
)
console.log("(3) Promise outer env ")
})();
}, 1000);
3. promise 多次被(单独)调用
// 3. 此为 promise 多次被(单独)调用
setTimeout(() => {
(function () {
// 第一次执行then 时获取的值,默认是resolve(值)的值, 后面的then,则需要通过返回或抛出获取值
const promise = new Promise((resolve, reject) => {
console.log("(1) executor area 同步回调执行, 发现resolve(3) 是异步函数,将其添加到异步队列中,[resolve(3)]")
resolve(3)
});
console.log("(2) 执行promise.then()同步函数, 发现内容是异步函数,将异步函数添加到异步队列中,[resolve(3),value1, reason1]")
promise.then(
value => { console.log("(6) this Promise return onResolved value1: resolve(3) =>3", value); return 4 },
reason => console.log("(6) Promise return onRejected reason1:", reason)
);
console.log("(3) 执行promise.then()同步函数, 发现内容是异步函数,将异步函数添加到异步队列中,[resolve(3),value1, reason1, value2, reason2]")
promise.then(
value => {
console.log("(7) this Promise return onResolved value2: resolve(3) =>3", value);
try {
throw new Error("5")
} catch (error) {
console.log("(8) this Promise throw exception: 5", error.message);
};
},
reason => console.log("(7) Promise return onRejected reason2:", reason)
);
console.log("(4) 执行promise.then()同步函数, 发现内容是异步函数,将异步函数添加到异步队列中,[resolve(3),value1, reason1, value2, reason2, value3, reason3]")
promise.then(
value => console.log("(9) this Promise return onResolved value3: resolve(3) =>3", value),
reason => {
console.log("(9) Promise return onRejected reason3:", reason);
try {
throw new Error("6")
} catch (error) {
console.log("(10) Promise throw exception:", error.message);
};
}
)
console.log("(5) Promise return:", promise)
})();
}, 2000);
4. promise 多次被链式调用
// 4. 此为 promise 多次被链式调用(链式调用,Promise.then()的结果作为下一个then()的value,而then()执行是否成功则作为下一个then()的回调选择)
setTimeout(() => {
(function () {
// 第一次执行then 时获取的值,默认是resolve(值)的值, 后面的then,则需要通过返回或抛出获取值
const promise = new Promise((resolve, reject) => {
console.log("(1) executor area 同步回调执行,发现resolve(3) 是异步函数,将其添加到异步队列中,[resolve(3)]")
resolve(4)
}).then(
value => { console.log("(4) this Promise return onResolved value1: resolve(4)=>4", value); return 5 },
reason => console.log("(4) Promise return onRejected reason1: ", reason)
).then(
value => {
console.log("(5) this Promise return onResolved value2: return 5", value);
throw new Error(6)
},
reason => console.log("(5) Promise return onRejected reason2:", reason)
).then(
value => console.log("(7) Promise return onResolved value3:", value),
reason => {
console.log("(7) this Promise return onRejected reason3: Error message", reason);
try {
throw new Error("7")
} catch (error) {
console.log("(8) this Promise throw exception: 7", error.message);
};
}
).then(
value => console.log("(9) this Promise return onResolved value4: undefined", value),
reason => console.log("(8) Promise return onRejected reason4:", reason)
)
console.log("(2) 遇见 4 次 then 同步调用函数, 均发现器内容是 异步调用函数,先后将异步函数添加到异步队列中:[resolve(3),value1, reason1, value2, reason2, value3, reason3,value4,reason4]")
console.log("(3) Promise return:", promise)
})();
}, 2200);
5.同步与异步操作(return Promise setTimeout)
setTimeout(() => {
(function () {
const promise = new Promise((resolve, reject) => {
console.log("(1) executor area env resolve 1, 添加到异步队列中: [resolve(1),]")
resolve(1)
}).then(
value1 => {
console.log("(2) resolve(1) onResolved return value1 1", value1)
setTimeout(() => {
console.log("(5) setTimeout value1 onResolved return ,添加到异步队列中[resolve(1),value1,value2,reason2,value1_setTimeout, return_setTimeout]")
})
return new Promise((resolve, reject) => {
console.log("(3) Promise executor area env resolve")
setTimeout(() => {
console.log("(6) return promise onResolved setTimeout")
reject(2)
// resolve(2)
})
})
}
).then(
value2 => console.log("(4)(7) setTimetout onResolved reject(2) reason2 2", value2),
reason2 => console.log("(4)(7) this setTimetout onRjected reject(2) reason2 2", reason2)
)
})();
}, 2400)
6.同步与异步操作(return setTimeout Promise)
setTimeout(() => {
(function () {
const promise = new Promise((resolve, reject) => {
console.log("(1) executor area env resolve 1, 添加到异步队列中: [resolve(1),]")
resolve(1)
}).then(
value1 => {
console.log("(2) resolve(1) onResolved return value1 1", value1)
setTimeout(() => {
console.log("(4) setTimeout value1 onResolved return ,添加到异步队列中")
})
return setTimeout(() => {
console.log("(5) value1 setTimeout")
return new Promise((resolve, reject) => {
console.log("(6) resolve ")
let r = resolve(2)
console.log("(7) resolve(2) return", r) // undefined
})
}) // return value is setTimeout ID
}
).then(
value2 => {
console.log("(3) setTimetout onResolved resolve(2) reason2 2",)
setTimeout(() => { console.log("(8) this get setTimeout id", value2) }, 300)
},
reason2 => console.log("(3) setTimetout onRjected reject(2) reason2 2", reason2)
)
})();
}, 3000)
7.异常穿透
// 异常穿透
const promise = new Promise((resolve, reject) => {
reject(1)
}).then(
value => console.log(value),
).then(
value => console.log(value),
reason => { return Promise.reject(reason) },
).then(
value => console.log(value),
reason => { throw reason }
).catch(
reason => console.log(reason)
)
8.中断promise链
// 中断Promise链
const promise1 = new Promise((resolve, reject) => {
reject(1)
}).then(
value => console.log(value),
).then(
value => console.log(value),
reason => { return new Promise(() => { }) }, // Promise status pending
).catch(
reason => console.log(reason)
)
第三章 自定义(手写) Promise
函数式(宏定义)
/*
暴露: es5 iife
方法:
Promise.all()
Promise.allSettled()
Promise.any()
Promise.race()
Promise.reject()
Promise.resolve()
Promise.prototype.then()
Promise.prototype.catch()
Promise.prototype.finally()
*/
(function (window) {
/*
1.Promise 构造函数
传入执行函数,
返回一个新的Promise对象
*/
const PENDING = "pending"
const RESOLVED = "resolved"
const REJECTED = "rejected"
const ONRESOLVED = "onResolved"
const ONREJECTED = "onRejected"
function Promise(executor) {
this.data = undefined
this.status = "pending"
this.callback = []
let that = this
function resolve(value) {
// 只有当 status 为 pending 时才继续执行
if (that.status !== PENDING) return
that.data = value
that.status = RESOLVED
// add_async_callback(value, ONRESOLVED)
if (that.callback.length > 0) {
setTimeout(() => {
that.callback.forEach((callbacksObj) => {
callbacksObj.onResolved(value)
})
})
}
}
function reject(reason) {
// 只有当 status 为 pending 时才继续执行
if (that.status !== PENDING) return
that.data = reason
that.status = REJECTED
// add_async_callback(reason, ONREJECTED)
if (that.callback.length > 0) {
setTimeout(() => {
that.callback.forEach((callbacksObj) => {
callbacksObj.onRjected(reason)
})
})
}
}
// 执行器函数调用,捕获异常
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
/*
2.Promise 原型上的方法 proptotype (then,catch, finally)
then:
- 传入onResolved,onRjected
- 返回一个新的Promise
catch:
- 传入onRjected
- 返回一个
*/
Promise.prototype.then = function (onResolved, onRjected) {
/*
1.如果抛出异常, return的promise就会失败,reason就是error
2.如果回调函数返回不是promise,return的promise就会成功,value就是返回的值
3.如果回调函数返回是promise,return的promise结果就是这个promise的结果
*/
let that = this
//指定默认的失败的回调(实现错误/异常传透的关键点)
onResolved = typeof onResolved === 'function' ? onResolved : value => value
onRjected = typeof onRjected === 'function' ? onRjected : reason => { throw reason }
return new Promise((resolve, reject) => {
function handle(callback) {
try {
const result = callback(that.data)
if (result instanceof Promise) {
result.then(resolve, reject)
} else {
resolve(result)
}
} catch (error) {
reject(error)
}
}
setTimeout(() => {
if (that.status === REJECTED) {
handle(onRjected)
} else if (that.status === RESOLVED) {
handle(onResolved)
} else {
that.callback.push({
onResolved(value) {
handle(onResolved)
},
onRjected(reason) {
handle(onRjected)
},
})
}
})
})
}
Promise.prototype.catch = function (onRjected) {
return this.then(undefined, onRjected)
}
Promise.prototype.finally = function () { }
Promise.resolve = function (value) {
return new Promise((resolve, reject) => {
if (value instanceof Promise) { value.then(resolve, reject) } else {
resolve(value)
}
})
}
Promise.reject = function (value) {
return new Promise((_, reject) => {
reject(value)
})
}
// 自定义 延时
Promise.resolveDelay = function (value, timeout) {
setTimeout(() => {
Promise.resolve(value);
}, timeout);
}
Promise.rejectDelay = function (reason, timeout) {
setTimeout(() => {
Promise.reject(reason);
}, timeout);
}
//返回一个promise,只有当所有proimse都成功时才成功,否则只要有一个失败的就失败
Promise.all = function (promises) {
// promises 是一个Promise队列, all 的功能就是,将队列中所有的Promise的状态 一假为假(返回状态为rejected的 reason)
const resolvedArray = new Array(promises.length);
let resolvedCount = 0;
return new Promise((resolve, reject) => {
promises.forEach((p, index) => {
Promise.resolve(p).then(
values => {
resolvedCount++
resolvedArray[index] = values
// 如果全部成功,则返回一个成功的Promise [value1, value2, ...]
if (resolvedCount === promises.length) {
resolve(resolvedArray)
}
},
reason => {
// 如果一个出错,直接返回第一个出错的Promise的reason
reject(reason)
});
})
})
}
//返回一个promise,其结果由第一个完成的promise决定
Promise.race = function (promises) {
return new Promise((resolve, reject) => {
promises.forEach(p => {
Promise.resolve(p).then(
value => {
resolve(value);
},
reason => {
reject(reason);
}
)
})
})
}
window.Promise = Promise;
})(window);
类方法(宏定义)
(function (window) {
const PENDING = "pending"
const RESOLVED = "resolved"
const REJECTED = "rejected"
ISFUCTION = fn => typeof fn === "function"
class MyPromise {
constructor(executor) {
this._status = PENDING
this._value = undefined
this._reason = undefined
this._fulfilledQueue = [];
this._rejectedQueue = [];
if (!ISFUCTION(executor)) {
throw new Error("Promise must accept a function as a parameter")
}
try {
executor(this._resolve.bind(this), this._reject.bind(this))
} catch (error) {
this._reject(error)
}
}
static resolve(value) {
return new MyPromise((resolve, reject) => {
if (value instanceof MyPromise) {
value.then(resolve, reject)
} else {
resolve(value)
}
})
}
static reject(reason) {
return new MyPromise((_, reject) => {
reject(reason)
})
}
static all(MyPromises) {
let resolveCount = 0
let resolvedAll = new Array(MyPromises.length)
return new MyPromise((resolve, reject) => {
MyPromises.forEach((p, index) => {
MyPromise.resolve(p).then(
value => {
resolveCount++
resolvedAll[index] = value
if (resolveCount === MyPromises.length) {
resolve(resolvedAll)
}
},
reason => {
reject(reason)
}
)
})
})
}
static race(MyPromises) {
return new MyPromise((resolve, reject) => {
MyPromises.forEach((p) => {
MyPromise.resolve(p).then(
value => {
resolve(value)
},
reason => {
reject(reason)
}
)
})
})
}
_resolve(value) {
if (this._status !== PENDING) return
this._status = RESOLVED
this._value = value
setTimeout(() => {
if (this._fulfilledQueue.length > 0) {
this._fulfilledQueue.forEach(callback => { callback.onResolved(value) })
}
})
}
_reject(reason) {
if (this._status !== PENDING) return
this._status = REJECTED
this._reason = reason
setTimeout(() => {
if (this._rejectedQueue.length > 0) {
this._rejectedQueue.forEach(callback => { callback.onRejected(reason) })
}
})
}
then(onResolved, onRejected) {
onResolved = typeof onResolved === 'function' ? onResolved : value => value
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
return new MyPromise((resolve, reject) => {
const handle = (callback, resolve, reject) => {
try {
const result = callback(this._value || this._reason)
if (result instanceof MyPromise) {
result.then(resolve, reject)
} else {
resolve(result)
}
} catch (error) {
reject(error)
}
}
setTimeout(() => {
if (this._status === REJECTED) {
handle(onRejected, resolve, reject)
} else if (this._status === RESOLVED) {
handle(onResolved, resolve, reject)
} else {
const ResolvedFn = { onResolved(value) { handle(onResolved, resolve, reject) } }
const RejectedFn = { onRejected(reason) { handle(onRejected, resolve, reject) } }
this._fulfilledQueue.push(ResolvedFn)
this._rejectedQueue.push(RejectedFn)
}
})
})
}
catch(onRjected) {
return this.then(undefined, onRjected)
}
}
window.MyPromise = MyPromise;
})(window);
第四章 宏队列与微队列

1.JS 中用来存储待执行回调函数的队列包含2个不同特定的列队
2.宏列队: 用来保存待执行的宏任务(回调),比如: 定时器回调/ DOM事件回调/ ajax回调
3.微列队: 用来保存待执行的微任务(回调),比如: promise的回调 / MutationObserver的回调
4.JS执行时会区别这两个队列
(1)JS引擎首先必须先执行所有的初始化同步任务代码
(2)每次准备取出第一个宏任务执行前,都要将所有的微任务一个一个取出来执行
优先执行顺序: 同步代码 > 微队列[微任务, ...] > 宏队列 [宏任务, ...]
/*
1.顺序宏任务添加到宏队列
2.同步任务 先执行
3.状态确定立即调用then, 将异步存储callback, 若前then未执行,将其缓存,等前then执行后再添加
4.状态未定,先等待
调用then(表示已经确定状态)
*/
第五章 面试题
一、面试题
面试题1
setTimeout(() => {
console.log(1)
}, 0)
Promise.resolve().then(() => {
console.log(2)
})
Promise.resolve().then(() => {
console.log(4)
})
console.log(3)
面试题2
setTimeout(() => {
console.log(1)
}, 0)
new Promise((resolve) => {
console.log(2)
resolve()
}).then(() => {
console.log(3)
}).then(() => {
console.log(4)
})
console.log(5)
面试题3
const first = () => (new Promise((resolve, reject) => {
console.log(3)
let p = new Promise((resolve, reject) => {
console.log(7)
setTimeout(() => {
console.log(5)
resolve(6)
}, 0)
resolve(1)
})
resolve(2)
p.then((arg) => {
console.log(arg)
})
}))
first().then((arg) => {
console.log(arg)
})
console.log(4)
面试题4
setTimeout(() => {
console.log("9")
}, 0)
new Promise((resolve, reject) => {
console.log("1")
resolve()
}).then(() => {
console.log("2")
new Promise((resolve, reject) => {
console.log("3")
resolve()
}).then(() => {
console.log("4")
}).then(() => {
console.log("5")
})
}).then(() => {
console.log("6")
})
new Promise((resolve, reject) => {
console.log("7")
resolve()
}).then(() => {
console.log("8")
})
面试题5
手写 promise
二、答案
面试题1:3 2 4 1
面试题2:2 5 3 4 1
面试题3:3 7 4 1 2 5
面试题4:1 7 2 3 8 4 6 5 9
手写promise见第三章

浙公网安备 33010602011771号