前端面试复习 3---异步与 Promise
计算机原理:进程与线程
- 进程:CPU 资源分配的最小单位
- 线程:CPU 调度的最小单位
- 解读:每一辆车代表我们日常用的每个程序,洗车场代表电脑的内存空间。每个停车位就代表了每个进程,小轿车占用小车位,大巴车占用大车位,等同于每个应用程序也会根基自身的需要的运行内存,分配不一样的进程空间。每个洗车工人代表一个线程,一个洗车位可以用多个洗车工人在干活,例如:洗车外面的,清理内饰的同时在进行;等同于一个应用程序的进程空间可以多个线程在同时干活,例如:网易云音乐,在播发音乐的同时,你还可以发表评论。同一个洗车位内的工人可以共用这个洗车位内的所以工具资源,同一个进程内的线程共用这个进程内的资源空间。一个车位至少一位洗车工,一个进程至少一个线程。
面试题:浏览器新开一个窗口,是进程还是线程?
- 答案:进程,从区别概念回答
- 发散 1:窗口(进程间)通信怎么通讯?storage、session、cookie => storage、cookie 的区别? => 怎么样操作 cookie,前端操作 cookie 有什么风险?=> cookie 的应用场景?
- 发散 2:浏览器原理?
浏览器原理-网页
- GUI 渲染线程:
- 解析 HTML CSS 构建 DOM 树->布局->绘制
- 与 JS 引擎线程互斥,当执行 JS 引擎线程时,GUI 渲染会被挂起,当任务队列空闲时,主线程才回去执行 GUI
- JS 引擎线程:
- 处理 JS,解析执行脚本
- 分配、处理、执行了待执行脚本同时,处理待执行事件,维护事件队列
- 阻塞 GUI 渲染。js 为何会阻塞 GUI 渲染:因为网页是单线程的,js 执行的时候,渲染会被挂起。
- 定时器触发线程:
- 异步定时器的处理和执行 - setTimeout / setInterval
- 接收 JS 引擎分配的定时器任务,并执行
- 处理完成后交于事件触发线程
- 异步 HTTP 请求线程:
- 异步执行请求类操作
- 接收 JS 引起线程异步请求操作
- 监听回调,交给事件触发线程做处理
- 事件触发线程:
- 接收所有来源的事件
- 将回调的事件依次加入到任务队列的队尾,交给 JS 引擎执行
event loop
事件循环(Event Loop)是 JavaScript 的执行机制之一,用于管理和调度异步任务的执行顺序。
事件循环的核心思想是基于一个事件队列(Event Queue)和一个调用栈(Call Stack)来实现。当 JavaScript 引擎执行代码时,同步任务会被直接放入调用栈中执行,而异步任务则会被放入事件队列中等待执行。
事件队列中的任务分为宏任务(macrotask)和微任务(microtask)两种类型。
当调用栈为空时,事件循环开始执行。首先,事件循环会将队列中的微任务一次性地全部取出并执行完毕,然后检查浏览器是否需要进行页面渲染。接下来,事件循环会从宏任务队列中取出一个任务,并放入调用栈中执行。当宏任务执行完毕后,再次执行微任务,然后重新渲染页面(如果需要),继续循环这个过程。
通过事件循环的机制,JavaScript 可以实现非阻塞的异步编程,使得程序能够同时处理多个任务,并提高了程序的响应性和性能。同时,了解事件循环的原理也有助于我们更好地理解 JavaScript 的执行过程,并编写出更高效的异步代码。
微任务与宏任务
宏任务(macro-task)和微任务(micro-task)是指在 JavaScript 执行过程中,不同类型的任务分类。
宏任务通常包括以下几种类型:
- 渲染事件(如页面加载、重新渲染)
- 用户交互事件(如点击、滚动)
- 定时器事件(如 setTimeout、setInterval)
- 网络请求完成、文件读写完成等 I/O 操作的回调
- 执行整体的 JavaScript 代码(同步代码)
而微任务则包括以下几种类型:
- Promise 的 resolve 回调函数
- MutationObserver 的回调函数
在事件循环中,当一个宏任务执行完毕后,事件循环会检查微任务队列,并将其中的所有微任务连续执行,直到微任务队列为空。这意味着,微任务会在当前宏任务执行结束后立即执行,而不会等待下一个宏任务。
具体来说,当浏览器遇到一个宏任务时,它会进入宏任务队列,并等待执行。而当浏览器遇到一个微任务时,它会立即执行,并将所有产生的新的微任务加入微任务队列中。
通过合理利用微任务,我们可以在宏任务的执行间隙执行一些高优先级的任务,从而提高页面的响应性和交互性。常见的应用场景是使用 Promise 进行异步操作,并在操作完成后立即执行相关的处理逻辑。
总结来说,宏任务表示较为宏观的、需要较长时间才能执行完毕的任务,而微任务则表示较小粒度、需要快速执行的任务。掌握宏任务和微任务的执行顺序和使用场景,可以帮助我们更好地处理异步代码和优化程序性能。
event loop 基本过程
- 初始化阶段:事件循环从全局上下文开始,并执行同步代码。任何遇到的异步任务将被放入相应的任务队列中。
- 宏任务阶段:执行异步任务队列中的微任务。
- 渲染阶段:如果 DOM 被改变,渲染 DOM。
- 宏任务阶段:在微任务阶段结束后,事件循环会从宏任务队列中选择一个任务。这个任务将被推送到调用栈中执行,直到任务执行完毕。
- 重复循环:重复进行微任务阶段、宏任务阶段和渲染阶段,直到没有更多的任务。
这就是事件循环的基本过程,它通过不断地处理微任务和宏任务,以及渲染阶段的操作,使得 JavaScript 能够处理异步任务并保持页面的响应性。注意,这只是一个简化的描述,实际的事件循环机制可能会有更多的细节和特殊情况。
graph LR
A[同步代码]
A --> |存放| B[微任务队列]
A --> |存放| C[宏任务队列]
B --> |拥有| B1[微任务 1]
B --> |拥有| B2[微任务 2]
B --> |拥有| B3[微任务..]
C --> |拥有| C1[宏任务 1]
C --> |拥有| C2[宏任务2]
C --> |拥有| C3[宏任务..]
D[微任务队列被全部执行]
B1 --> |执行| D
B2 --> |执行| D
B3 --> |执行| D
D --> E[DOM如果被改变就重新渲染]
E --> |所有微任务执行完后执行| C1 --> |event loop| C2 --> |event loop| C3
上面整个流程图就是 event loop 的流程,在执行宏任务 1时会继续产生一个这样的 event loop 流程,直至宏任务 1被执行完,然后执行宏任务 2再产生这样的 event loop 流程,直到所有宏任务执行结束
执行栈/调用栈示例 demo
function fn2() {
throw new Error('抛出一个错误')
}
function fn1() {
fn2()
}
function run() {
fn1()
console.log('fn2抛出错误后,这里已经不执行了')
}
run()
// 执行栈后进先出
// 执行栈:run->fn1->fn2
// 执行完成顺序:fn2->fn1->run
// 先执行完成fn2再往下执行fn1,执行完成fn1再往下执行run,因为fn2报错了,所以fn1和run都无法往下执行完成
面试题 1:死循环
js 堆栈执行顺序与堆栈溢出/ 爆栈 / 性能卡顿 / => js 性能优化
死循环例子:
function fn() {
fn()
}
fn()
正确情况下不会写出这样的死循环,如果在 vue 的 computed 中, 对某个和 computed 有关系的的变量进行复制就会造成这样的死循环。 例如:
{
data() {
return {
bbb: 222,
ccc: 333,
}
},
computed: {
aaa() {
this.bbb = 111
return this.bbb + this.ccc
}
}
}
面试题 2:执行顺序
setTimeout(() => {
console.log('setTimeout') // 5. 宏任务2
})
new Promise((resolve, reject) => {
console.log('new Promise') // 1. 属于同步进入主线程 宏任务1
resolve()
})
.then(() => {
console.log('Promise then') // 3. 微任务1
})
.then(() => {
console.log('Promise then then') // 4. 微任务2
})
console.log('hi') // 2. 同步代码 宏任务1
解题思路:任务维度
promise 有哪些状态?
- pending/待处理的, fulfilled/满足的, rejected/被驳回的
- executor: new Promise() 的时候立即执行,接收两个参数 resolve, reject
promise 的默认状态是什么?状态是如何流转的?
- 默认:pending
- 状态流转:pending => fulfilled | rejected
手写 Promise
Promise 规范
- Promise 实例有三个状态: pending fulfilled rejected,默认状态为 pending
- 状态只能从 pending 向 fulfilled | rejected 流转
- Promise(executor) 接收一个参数 executor
- executor(resolve, reject) 是同步执行的,同时接收两个两个参数 resolve reject
- resolve(value) 成功的回调,接收一个任何类型的参数作为成功的返回值,执行 resolve(value) 会把实例的状态变为 fulfilled
- reject(reason) 失败的回调,接收一个参数通常是字符串作为失败的原因,执行 reject(reason) 会把实例的状态变为 rejected
- 实例内部需要有一个 result 属性,用来存储 resolve(value) reject(reason) 传进来的的 value 或 reason
- Promise 的 then(onResolved, onRejected) 方法接收两个参数 onResolved, onRejected,目的是为了可以拿到内部的 result
- 当状态为 fulfilled 时执行 onResolved,result 为 resolve(value) 传进来的 value
- 当状态为 rejected 时执行 onRejected,result 为 reject(reason) 传进来的 reason
- then 返回的是一个 Promise 实例,这个实例的状态是由 onResolved onRejected 的返回结果决定的,onResolved onRejected 的返回结果不是一个 Promise 实例时,then 方法都会返回一个状态为 fulfilled 的 Promise 实例, onResolved onRejected 的返回结果是一个 Promise 实例时,这个 实例的状态就是 then 方法返回实例的状态
- Promise 的 catch(onRejected) 方法接收一个参数 onRejected
- catch 返回的同样是一个 Promise 实例,这个实例的状态是由 onRejected 的返回结果决定的
1. 基本框架
class Promise {
// 用静态属性定义三个状态,方便后面使用
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(executor) {
this.result = undefined // 存储 resolve(value) reject(reason) 传进来的的 value 或 reason
this.state = Promise.PENDING // 默认状态为 pending
const resolve = (value) => {
if (this.state === Promise.PENDING) {
this.state = Promise.FULFILLED
this.result = value
}
}
const reject = (reason) => {
if (this.state === Promise.PENDING) {
this.state = Promise.REJECTED
this.result = reason
}
}
// 这里用 try catch 是为了解决像 p3 的情况,在 executor 实参的函数体中有报错
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
}
const p1 = new Promise((resolve, reject) => {
resolve('test')
})
console.log(p1)
const p2 = new Promise((resolve, reject) => {
reject('test')
})
console.log(p2)
const p3 = new Promise((resolve, reject) => {
throw 'test'
})
console.log(p3)
2. 添加 then 方法
class Promise {
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(executor) {
this.result = undefined
this.state = Promise.PENDING
const resolve = (value) => {
if (this.state === Promise.PENDING) {
this.state = Promise.FULFILLED
this.result = value
}
}
const reject = (reason) => {
if (this.state === Promise.PENDING) {
this.state = Promise.REJECTED
this.result = reason
}
}
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
// 添加 then 方法,参数 onResolved, onRejected 函数是为了拿到内部维护的 this.result
then(onResolved, onRejected) {
// onResolved, onRejected 必须是函数如果不是把它们转为函数
if (typeof onResolved !== 'function') onResolved = (value) => value
if (typeof onRejected !== 'function')
onRejected = (reason) => {
throw reason
}
if (this.state === Promise.FULFILLED) {
onResolved(this.result) // 将内部的 this.result 传出去
} else if (this.state === Promise.REJECTED) {
onRejected(this.result)
}
}
}
/**
* executor 中的 resolve('ok') 将内部的 this.result 维护成 'ok'
* 再通过 then(onResolved) 的 onResolved 将 this.result 拿到
*/
const p1 = new Promise((resolve, reject) => {
resolve('ok')
})
p1.then((value) => {
console.log(value)
})
const p2 = new Promise((resolve, reject) => {
reject('fail')
})
p2.then(null, (reason) => {
console.log(reason)
})
3. then 方法添加异步的情况
class Promise {
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(executor) {
this.result = undefined
this.state = Promise.PENDING
// 1. 存 then(onResolved, onRejected) 传进来的两个参数函数
this.callback = {
onResolved: null,
onRejected: null
}
const resolve = (value) => {
if (this.state === Promise.PENDING) {
this.state = Promise.FULFILLED
this.result = value
if (this.callback.onResolved) {
// 3. 执行在 then 方法存进来的 onResolved
this.callback.onResolved(value)
}
}
}
const reject = (reason) => {
if (this.state === Promise.PENDING) {
this.state = Promise.REJECTED
this.result = reason
if (this.callback.onRejected) {
this.callback.onRejected(reason)
}
}
}
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
then(onResolved, onRejected) {
if (typeof onResolved !== 'function') onResolved = (value) => value
if (typeof onRejected !== 'function')
onRejected = (reason) => {
throw reason
}
if (this.state === Promise.FULFILLED) {
onResolved(this.result)
} else if (this.state === Promise.REJECTED) {
onRejected(this.result)
} else if (this.state === Promise.PENDING) {
// 2. resolve reject 在 setTimeout 里面是异步代码,执行 then 的时候它们俩还没执行,实例的状态还是 pending
// 这种情况现将 onResolved onRejected 存起来交到 resolve reject 再执行
this.callback = { onResolved, onRejected }
}
}
}
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('resolve in setTimeout')
}, 1000)
})
p1.then((value) => {
console.log(value)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('reject in setTimeout')
}, 2000)
})
p2.then(null, (reason) => {
console.log(reason)
})
4. 实例多次调用 then 的情况
class Promise {
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(executor) {
this.result = undefined
this.state = Promise.PENDING
// 1. this.callbacks 改成数组,可以存多个 { onResolved, onRejected } 这样的对象
this.callbacks = []
const resolve = (value) => {
if (this.state === Promise.PENDING) {
this.state = Promise.FULFILLED
this.result = value
// 3. 遍历执行在 then 方法存进来的 onResolved
this.callbacks.forEach((item) => {
item.onResolved(value)
})
}
}
const reject = (reason) => {
if (this.state === Promise.PENDING) {
this.state = Promise.REJECTED
this.result = reason
this.callbacks.forEach((item) => {
item.onRejected(reason)
})
}
}
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
then(onResolved, onRejected) {
if (typeof onResolved !== 'function') onResolved = (value) => value
if (typeof onRejected !== 'function')
onRejected = (reason) => {
throw reason
}
if (this.state === Promise.FULFILLED) {
onResolved(this.result)
} else if (this.state === Promise.REJECTED) {
onRejected(this.result)
} else if (this.state === Promise.PENDING) {
// 2. 执行 then 的时候,如果状态还是 pending,
// onResolved, onRejected 先通过 this.callbacks 存起来,交到 resolve reject 再执行
this.callbacks.push({ onResolved, onRejected })
}
}
}
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('resolve in setTimeout')
}, 1000)
})
p1.then((value) => {
console.log('1 ' + value)
})
p1.then((value) => {
console.log('2 ' + value)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('reject in setTimeout')
}, 1000)
})
p2.then(null, (reason) => {
console.log('1 ' + reason)
})
p2.then(null, (reason) => {
console.log('2 ' + reason)
})
5. then 方法返回一个新的 Promise 实例
class Promise {
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(executor) {
this.result = undefined
this.state = Promise.PENDING
this.callbacks = []
const resolve = (value) => {
if (this.state === Promise.PENDING) {
this.state = Promise.FULFILLED
this.result = value
this.callbacks.forEach((item) => {
item.onResolved(value)
})
}
}
const reject = (reason) => {
if (this.state === Promise.PENDING) {
this.state = Promise.REJECTED
this.result = reason
this.callbacks.forEach((item) => {
item.onRejected(reason)
})
}
}
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
/**
* 原本的思路不变, 还是通过 onResolved, onRejected 将 this.result 传出去,
* 但是这里同时要拿到 onResolved, onRejected 的返回结果,
* 根据这个结果,然后通过被返回的实例的 resolve 和 reject 改变这个被返回的实例的状态和返回的值
*/
then(onResolved, onRejected) {
if (typeof onResolved !== 'function') onResolved = (value) => value
if (typeof onRejected !== 'function')
onRejected = (reason) => {
throw reason
}
return new Promise((resolve, reject) => {
if (this.state === Promise.FULFILLED) {
try {
const res = onResolved(this.result)
if (res instanceof Promise) {
// onResolved 的返回结果是一个 Promise 实例时,onResolved 的返回实例的状态就是 then 方法返回实例的状态
res.then(
(v) => {
resolve(v)
},
(r) => {
reject(r)
}
)
} else {
// onResolved 的返回结果不是一个 Promise 实例时,then 方法都会返回一个状态为 fulfilled 的 Promise 实例
resolve(res)
}
} catch (error) {
// onResolved 中有报错的时候会进入这里
reject(error)
}
} else if (this.state === Promise.REJECTED) {
onRejected(this.result)
} else if (this.state === Promise.PENDING) {
this.callbacks.push({ onResolved, onRejected })
}
})
}
}
const p1 = new Promise((resolve) => {
resolve('onResolved 没有 return 即返回 undefined')
})
const p11 = p1.then((value) => {
value
// return value
})
console.log(p11)
const p2 = new Promise((resolve, reject) => {
resolve('onResolved 返回一个状态为 fulfilled 的 promise')
// reject('onRejected 返回一个状态为 rejected 的 promise')
})
const p22 = p2.then((value) => {
return new Promise((resolve) => {
resolve(value)
})
})
console.log(p22)
const p3 = new Promise((resolve) => {
resolve('onResolved 有报错')
})
const p33 = p3.then((value) => {
return new Promise((resolve, reject) => {
throw value
})
})
console.log(p33)
6. then 方法返回一个新的 Promise 实例时也考虑异步的情况
class Promise {
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(executor) {
this.result = undefined
this.state = Promise.PENDING
this.callbacks = []
const resolve = (value) => {
if (this.state === Promise.PENDING) {
this.state = Promise.FULFILLED
this.result = value
this.callbacks.forEach((item) => {
item.onResolved(value)
})
}
}
const reject = (reason) => {
if (this.state === Promise.PENDING) {
this.state = Promise.REJECTED
this.result = reason
this.callbacks.forEach((item) => {
item.onRejected(reason)
})
}
}
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
then(onResolved, onRejected) {
if (typeof onResolved !== 'function') onResolved = (value) => value
if (typeof onRejected !== 'function')
onRejected = (reason) => {
throw reason
}
return new Promise((resolve, reject) => {
if (this.state === Promise.FULFILLED) {
try {
const res = onResolved(this.result)
if (res instanceof Promise) {
res.then(
(v) => {
resolve(v)
},
(r) => {
reject(r)
}
)
} else {
resolve(res)
}
} catch (error) {
reject(error)
}
} else if (this.state === Promise.REJECTED) {
onRejected(this.result)
} else if (this.state === Promise.PENDING) {
/**
* 和之前一样的是:遇到 pending 的时候先把 onResolved 通过 callbacks 存起来
* 不一样的是:之前只要执行 onResolved 把值传出去,现在要多执行 resolve 改变被返回 Promise 实例的状态
*/
this.callbacks.push({
onResolved: () => {
// const res = onResolved(this.result)
// resolve(res)
// 如果不考虑 onResolved 返回 Promise 和 执行中报错的情况只要上面两句即可
try {
const res = onResolved(this.result)
if (res instanceof Promise) {
res.then(
(v) => {
resolve(v)
},
(r) => {
reject(r)
}
)
} else {
resolve(res)
}
} catch (error) {
reject(error)
}
},
onRejected: () => {
try {
const res = onRejected(this.result)
if (res instanceof Promise) {
res.then(
(v) => {
resolve(v)
},
(r) => {
reject(r)
}
)
} else {
resolve(res)
}
} catch (error) {
reject(error)
}
}
})
}
})
}
}
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
// resolve('resolve in setTimeout')
reject('reject in setTimeout')
}, 1000)
})
const p11 = p1.then(
(value) => {
return value
// throw value
},
(reason) => {
return reason
}
)
console.log(p11)
7. then 方法,代码优化
class Promise {
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(executor) {
this.result = undefined
this.state = Promise.PENDING
this.callbacks = []
const resolve = (value) => {
if (this.state === Promise.PENDING) {
this.state = Promise.FULFILLED
this.result = value
this.callbacks.forEach((item) => {
item.onResolved(value)
})
}
}
const reject = (reason) => {
if (this.state === Promise.PENDING) {
this.state = Promise.REJECTED
this.result = reason
this.callbacks.forEach((item) => {
item.onRejected(reason)
})
}
}
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
then(onResolved, onRejected) {
if (typeof onResolved !== 'function') onResolved = (value) => value
if (typeof onRejected !== 'function')
onRejected = (reason) => {
throw reason
}
return new Promise((resolve, reject) => {
/**
* 回调逻辑抽离
* @param {onResolved, onRejected } type
*/
const callback = (type) => {
try {
const res = type(this.result)
if (res instanceof Promise) {
res.then(
(v) => {
resolve(v)
},
(r) => {
reject(r)
}
)
} else {
resolve(res)
}
} catch (error) {
reject(error)
}
}
if (this.state === Promise.FULFILLED) {
callback(onResolved)
} else if (this.state === Promise.REJECTED) {
callback(onRejected)
} else if (this.state === Promise.PENDING) {
this.callbacks.push({
onResolved: () => {
callback(onResolved)
},
onRejected: () => {
callback(onRejected)
}
})
}
})
}
}
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
// resolve('resolve in setTimeout')
reject('reject in setTimeout')
}, 1000)
}).then(
(value) => {
return value
// throw value
},
(reason) => {
return reason
}
)
console.log(p1)
const p2 = new Promise((resolve, reject) => {
resolve('ok')
// reject('fail')
}).then(
(value) => {
return value
// throw value
},
(reason) => {
return reason
}
)
console.log(p2)
8. 类方法 resolve reject
class Promise {
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(executor) {
this.result = undefined
this.state = Promise.PENDING
this.callbacks = []
const resolve = (value) => {
if (this.state === Promise.PENDING) {
this.state = Promise.FULFILLED
this.result = value
this.callbacks.forEach((item) => {
item.onResolved(value)
})
}
}
const reject = (reason) => {
if (this.state === Promise.PENDING) {
this.state = Promise.REJECTED
this.result = reason
this.callbacks.forEach((item) => {
item.onRejected(reason)
})
}
}
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
then(onResolved, onRejected) {
if (typeof onResolved !== 'function') onResolved = (value) => value
if (typeof onRejected !== 'function')
onRejected = (reason) => {
throw reason
}
return new Promise((resolve, reject) => {
const callback = (type) => {
try {
const res = type(this.result)
if (res instanceof Promise) {
res.then(
(v) => {
resolve(v)
},
(r) => {
reject(r)
}
)
} else {
resolve(res)
}
} catch (error) {
reject(error)
}
}
if (this.state === Promise.FULFILLED) {
callback(onResolved)
} else if (this.state === Promise.REJECTED) {
callback(onRejected)
} else if (this.state === Promise.PENDING) {
this.callbacks.push({
onResolved: () => {
callback(onResolved)
},
onRejected: () => {
callback(onRejected)
}
})
}
})
}
catch(onRejected) {
return this.then(null, onRejected)
}
// 添加类方法 resolve,这里换了个思路: 如果入参是 promise 直接返回去
static resolve(value) {
if (value instanceof Promise) {
return value
} else {
return new Promise((resolve, reject) => {
resolve(value)
})
}
}
// 旧思路,添加类方法 resolve
static resolve1(value) {
return new Promise((resolve, reject) => {
if (value instanceof Promise) {
value.then(
(v) => {
resolve(v)
},
(r) => {
reject(r)
}
)
} else {
resolve(value)
}
})
}
// 添加类方法 reject
static reject(reason) {
return new Promise((resolve, reject) => {
reject(reason)
})
}
}
const p1 = Promise.resolve('ok')
console.log(p1)
const p2 = Promise.resolve(
new Promise((resolve, reject) => {
// resolve('ok')
reject('err')
})
)
console.log(p2)
const p3 = Promise.reject('error')
console.log(p3)
const p4 = Promise.reject(
new Promise((resolve, reject) => {
resolve('ok-p4')
})
)
console.log(p4)
9. 类方法 all race
class Promise {
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(executor) {
this.result = undefined
this.state = Promise.PENDING
this.callbacks = []
const resolve = (value) => {
if (this.state === Promise.PENDING) {
this.state = Promise.FULFILLED
this.result = value
this.callbacks.forEach((item) => {
item.onResolved(value)
})
}
}
const reject = (reason) => {
if (this.state === Promise.PENDING) {
this.state = Promise.REJECTED
this.result = reason
this.callbacks.forEach((item) => {
item.onRejected(reason)
})
}
}
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
then(onResolved, onRejected) {
if (typeof onResolved !== 'function') onResolved = (value) => value
if (typeof onRejected !== 'function')
onRejected = (reason) => {
throw reason
}
return new Promise((resolve, reject) => {
const callback = (type) => {
try {
const res = type(this.result)
if (res instanceof Promise) {
res.then(
(v) => {
resolve(v)
},
(r) => {
reject(r)
}
)
} else {
resolve(res)
}
} catch (error) {
reject(error)
}
}
if (this.state === Promise.FULFILLED) {
callback(onResolved)
} else if (this.state === Promise.REJECTED) {
callback(onRejected)
} else if (this.state === Promise.PENDING) {
this.callbacks.push({
onResolved: () => {
callback(onResolved)
},
onRejected: () => {
callback(onRejected)
}
})
}
})
}
catch(onRejected) {
return this.then(null, onRejected)
}
static resolve(value) {
return new Promise((resolve, reject) => {
if (value instanceof Promise) {
value.then(
(v) => {
resolve(v)
},
(r) => {
reject(r)
}
)
} else {
resolve(value)
}
})
}
// 添加类方法 reject
static reject(reason) {
return new Promise((resolve, reject) => {
reject(reason)
})
}
// 添加类方法 all
static all(promises) {
return new Promise((resolve, reject) => {
let count = 0
const list = []
for (let i = 0; i < promises.length; i++) {
promises[i].then(
(v) => {
count++
list[i] = v
if (count === promises.length) {
resolve(list)
}
},
(r) => {
reject(r)
}
)
}
})
}
// 添加类方法 race
static race(promises) {
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(
(v) => {
resolve(v)
},
(r) => {
reject(r)
}
)
}
})
}
}
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('ok1')
})
})
const p2 = Promise.resolve('ok2')
// const p3 = Promise.reject('error')
const all = Promise.all([p1, p2])
console.log(all)
const race = Promise.race([p1, p2])
console.log(race)
10. 完结 + 优化细节 then(onResolve, onRejected) 中的 onResolve, onRejected 是异步执行的
class Promise {
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(executor) {
this.result = undefined
this.state = Promise.PENDING
this.callbacks = []
const resolve = (value) => {
if (this.state === Promise.PENDING) {
this.state = Promise.FULFILLED
this.result = value
// 添加异步执行
setTimeout(() => {
this.callbacks.forEach((item) => {
item.onResolved(value)
})
})
}
}
const reject = (reason) => {
if (this.state === Promise.PENDING) {
this.state = Promise.REJECTED
this.result = reason
// 添加异步执行
setTimeout(() => {
this.callbacks.forEach((item) => {
item.onRejected(reason)
})
})
}
}
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
then(onResolved, onRejected) {
if (typeof onResolved !== 'function') onResolved = (value) => value
if (typeof onRejected !== 'function')
onRejected = (reason) => {
throw reason
}
return new Promise((resolve, reject) => {
const callback = (type) => {
try {
const res = type(this.result)
if (res instanceof Promise) {
res.then(
(v) => {
resolve(v)
},
(r) => {
reject(r)
}
)
} else {
resolve(res)
}
} catch (error) {
reject(error)
}
}
if (this.state === Promise.FULFILLED) {
// 添加异步执行
setTimeout(() => {
callback(onResolved)
})
} else if (this.state === Promise.REJECTED) {
// 添加异步执行
setTimeout(() => {
callback(onRejected)
})
} else if (this.state === Promise.PENDING) {
this.callbacks.push({
onResolved: () => {
callback(onResolved)
},
onRejected: () => {
callback(onRejected)
}
})
}
})
}
catch(onRejected) {
return this.then(null, onRejected)
}
static resolve(value) {
return new Promise((resolve, reject) => {
if (value instanceof Promise) {
value.then(
(v) => {
resolve(v)
},
(r) => {
reject(r)
}
)
} else {
resolve(value)
}
})
}
static reject(reason) {
return new Promise((resolve, reject) => {
reject(reason)
})
}
static all(promises) {
return new Promise((resolve, reject) => {
let count = 0
const list = []
for (let i = 0; i < promises.length; i++) {
promises[i].then(
(v) => {
count++
list[i] = v
if (count === promises.length) {
resolve(list)
}
},
(r) => {
reject(r)
}
)
}
})
}
static race(promises) {
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(
(v) => {
resolve(v)
},
(r) => {
reject(r)
}
)
}
})
}
}
const p1 = new Promise((resolve, reject) => {
resolve('ok1')
// reject('ok1')
console.log(111)
})
p1.then(
(v) => {
console.log('222-1')
},
(r) => {
console.log('222-2')
}
)
console.log(333)
async await & generator
async await
function p() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('延时1秒执行')
}, 1000)
})
}
async function asyncCall() {
const result = await p()
console.log('asyncCall---', result)
}
asyncCall()
generator 函数
- generator 就是生成器函数。是协程在 es6 中的一个实现。它最大的特征就是可以交出函数的执行权。
- generator 函数的执行和传统的不一样,它的返回值是一个迭代器对象。
- 简单理解就是返回值是一个对象,有两个属性,一个是
value
,一个是done
,vlaue
就是返回的值,done
布尔类型,false
代表还没完成迭代,true
代表完成了迭代。 - 遇到
yield
就会暂停,执行next()
就会继续执行并返回一个迭代器对象。
PS: 协成
- 协程是一种基于线程之上,但又比线程更加轻量级的存在。对内核来说具有不可见性。
- 一个进程可以有多个线程。一个线程可以有多个协程。
简单使用
function* fn() {
yield 1
yield 2
yield 3
}
const gen = fn()
console.log(gen.next()) // {value: 1, done: false}
console.log(gen.next()) // {value: 2, done: false}
console.log(gen.next()) // {value: 3, done: false}
console.log(gen.next()) // {value: undefined, done: true}
结合 promise 请求
function* generatorFn() {
const run1 = yield request(1)
console.log('run1---', run1) // 执行第二next()的时候这里才会执行, run1 等于第二个 next() 传进来的参数
const run2 = yield request(2)
console.log('run2---', run2)
const run3 = yield request(3)
console.log('run3---', run3)
const run4 = yield request(4)
console.log('run4---', run4)
const run5 = yield 'ok'
console.log('run5---', run5)
}
// generatorFn 里面虽然有多个异步请求,但是我们可以看到代码样式是同步的
function run(generator) {
const iterator = generator()
function next(res) {
let { value, done } = iterator.next(res)
if (!(value instanceof Promise)) {
console.log(`value是${value},不是promise对象`)
value = Promise.resolve(value)
}
if (done) {
console.log('done---', done)
return
}
value.then(next)
}
next()
}
run(generatorFn)
总结
- 其实 generator 在我们实际开发中并不常见,因为它当时主要用于 js 的异步解决方案,但是后来被更好的 async await 替代了,async await 也是 js 异步的终极解决方案。
- 但是 generator 独特的特性可以让我们在函数执行的过程中传递参数获取结果,使得函数调用变得更加灵活。