API 用法和实现
构造器
- Promise 构造器接收一个函数作为参数,该函数有两个参数
- resolve 将 promise 由 pending 状态转为 fulfilled 状态的方法
- reject 将 promise 由 pending 状态转为 rejected 状态的方法
- Promise 构造器一经调用,传给构造器的函数参数就会立即执行
- Promise 的状态一旦改变就不可再变
let p = new Promise((resolve, reject) => {
console.log(1)
setTimeout(() => {
console.log(6)
resolve(2)
}, 0)
console.log(3)
reject(4)
console.log(5)
})
执行结果如下:
1
3
5
// error: Uncaught (in promise) 4
6
Promise.prototype.then
- then 方法返回值是一个新的 Promise 实例
- then 方法接受两个函数作为参数,且这两个参数是可选的
- 一个是 promise 变为 fulfilled 状态后的回调,回调的参数是 promise resolve 时传入的值
- 一个是 promise 变为 rejected 状态后的回调,回调的参数是 promise reject 时传入的拒因或者是未被 catch 的错误
- 如果两个参数都不传,那么 then 返回的新 Promise 对象就会接受调用这个 then 的原 Promise 的终态作为它的终态
- then 方法支持链式回调,链式回调的实质是,then 的返回值是一个新的 Promise 实例
- 可以在同一个 Promise 实例上多次调用 then,最终的表现就是,会将所有的 then 中的回调放到一个队列中,当状态改变后,按顺序挨个调用
let p1 = new Promise((resolve) => {
console.log('start')
resolve(1)
})
.then((res) => {
console.log('>>>' + res)
return 2
})
.then((res) => {
console.log('===' + res)
return Promise.reject(res)
})
.then(
(res) => {
console.log('---' + res)
},
(reason) => {
console.log('reject:' + reason)
}
)
// 多次调用then 方法
p1.then(() => {
console.log('p1_2')
})
p1.then(() => {
console.log('p1_3')
})
console.log('end')
执行结果如下:
// start
// end
// >>> 1
// === 2
// reject: 2
// p1_2
// p1_3
- then 方法的调用是同步的,then 方法被调用之后,根据不同状态,会把传给它的回调放到了微任务队列里或者自身callback数组里
- 为什么这里的
p1_2和p1_3是在最后输出的?(注意p1的引用变化)
Promise.prototype.catch
- catch 方法的实质是 then 方法只提供失败的回调
- 该方法也返回一个新的 promise,因为本质上它就是 then 方法
let p = new Promise((resolve, reject) => {
reject('error 1')
})
.catch((e) => {
console.log('catch 1:' + e)
})
.catch((e) => {
console.log('catch 2:' + e)
throw 'error 2'
})
// catch 方法的本质
.then(undefined, (e) => {
console.log(e)
})
执行结果如下:
'error 1'
'error 2'
catch 的原理
Promise.prototype.catch = function (onRejected) {
return this.then(undefined, onRejected)
}
Promise.prototype.finally
- finally 方法是无论 Promise 实例的最终状态是什么,都会被调用的方法
- 这避免了同样的语句需要在 then()和 catch()中各写一次的情况。
- finally 方法也有返回值,返回一个设置了 finally 回调函数的 Promise 对象
- 由于无法知道 promise 的最终状态,所以 finally 的回调函数中不接收任何参数
let p = new Promise((resolve, reject) => {
setTimeout(() => resolve(1), 1000)
})
.finally(() => {
console.log('finally')
})
.then((res) => {
console.log(res)
})
输出结果如下:
finally
1
finally 的原理
Promise.prototype.finally = function (fn) {
// 因为需要透传,所以必须调用this上的then方法
return this.then(
(value) => {
// 放到resolve方法中执行,是因为fn中可能存在异步操作
return Promise.resolve(fn()).then(() => {
// 透传value
return value
})
},
(err) => {
return Promise.resolve(fn()).then(() => {
// 异常穿透
throw err
})
}
)
}
Promise.resolve
- resolve 方法返回一个以给定值解析后的 Promise 对象
- 如果这个值是一个 promise ,那么将返回这个 promise
- 如果这个值是 thenable(即带有"then" 方法),返回的 promise 会“跟随”这个 thenable 的对象,采用它的最终状态
- 否则返回的 promise 将以此值完成
// 值是 promise 实例的情况,将直接返回这个 promise 实例
let p1 = Promise.resolve(
new Promise((resolve) => {
resolve(1)
})
)
p1.then((res) => {
console.log(res) // 1
})
// 值是 thenable 对象的情况
let p2 = Promise.resolve({
then: function (onFulfill, onReject) {
onFulfill('fulfilled!')
},
})
p2.then((res) => {
console.log(res) // fulfilled
})
// 值是其他值的情况
let p3 = Promise.resolve(100)
p3.then((res) => {
console.log(res) // 100
})
resolve 原理
Promise.resolve = function (data) {
// 如果是一个Promise实例,直接返回
if (data instanceof Promise) return data
return new Promise((resolve, reject) => {
// 如果是一个thenable对象,那返回的promise的状态将跟随这个thenable对象的状态
if (data.then && typeof data.then === 'function') {
return data.then(resolve, reject)
}
// 其他情况,则直接返回
resolve(data)
})
}
Promise.reject
- 静态方法 reject 返回一个带有拒绝原因的 Promise 对象
reject 原理
Promise.reject = function (reason) {
return new Promise((_, reject) => reject(reason))
}
Promise.all
- 只有当传入的所有 promise 实例都 resolve,该方法返回的 promise 实例才 resolve,并将每个 promise 的结果依次放入结果数组中返回
- 一旦有一个 promise 被 reject,该方法返回的 promise 就 reject,且返回第一个被 reject 的 promise 的拒因
let genPromise = function (data, reason) {
return new Promise((resolve, reject) => {
return reason ? reject(reason) : resolve(data)
})
}
let p1 = genPromise(1)
let p2 = genPromise(2)
let p3 = genPromise(3)
Promise.all([p1, p2, p3]).then((res) => {
console.log(res) // [1, 2, 3]
})
let p4 = genPromise(undefined, 4)
let p5 = genPromise(5)
let p6 = genPromise(undefined, 6)
Promise.all([p4, p5, p6]).then(undefined, (reason) => {
console.log(reason) // 4
})
all 原理
Promise.all = function (promises) {
return new Promise((resolve, reject) => {
if (!Array.isArray(arr))
return reject(new TypeError('argument must be an Array'))
if (!arr.length) return resolve([])
let results = []
let len = promises.length
for (let i = 0; i < len; i++) {
promises[i].then(
(value) => {
// 不能直接push,因为不能保证每个promise返回的顺序
// 错误:results.push(value)
results[i] = value
// 都得到结果之后就resolve
if (!--len) resolve(results)
},
(reason) => {
reject(reason)
}
)
}
})
}
Promise.any
- 目前处于 Stage4 阶段
- 接收一个 Promise 可迭代对象
- 只要其中的一个 promise 成功,就返回那个已经成功的 promise,不会等待其他的 promise 全部完成
- 如果可迭代对象中没有一个 promise 成功,就返回一个失败的 promise 和 AggregateError 类型的实例,它是 Error 的一个子类,用于把单一的错误集合在一起
let genPromise = function (data, reason) {
return new Promise((resolve, reject) => {
return reason ? reject(reason) : resolve(data)
})
}
// 只要有一个成功,就返回那个成功的
let p1 = genPromise(1)
let p2 = genPromise(undefined, 2)
let p3 = genPromise(3)
Promise.any([p1, p2, p3]).then((res) => {
console.log(res) // 1
})
// 所有都不成功,则返回一个失败的promise
let p4 = genPromise(undefined, 4)
let p5 = genPromise(undefined, 5)
let p6 = genPromise(undefined, 6)
Promise.any([p4, p5, p6]).then(undefined, (reason) => {
console.log(reason) // 'AggregateError: All promises were rejected'
})
any 原理
- 如果传入的参数是一个空的可迭代对象,则返回一个 已失败(already rejected) 状态的 Promise。
- 如果传入的参数不包含任何 promise,则返回一个 异步完成 (asynchronously resolved)的 Promise。
Promise.any = function (promises) {
return new Promise((resolve, reject) => {
if (!Array.isArray(arr))
return reject(new TypeError('argument must be an Array'))
if (!arr.length)
return reject(new AggregateError('All promises were rejected'))
let len = promises.len
for (let p of promises) {
p.then(
(value) => {
resolve(value)
},
(reject) => {
if (!--len)
return reject(new AggregateError('All promises were rejected'))
}
)
}
})
}
Promise.race
- 返回一个 promise,一旦迭代器中的某个 promise 解决或拒绝,返回的 promise 就会解决或拒绝
let genPromise = function (data, reason) {
return new Promise((resolve, reject) => {
return reason ? reject(reason) : resolve(data)
})
}
// 一旦某个 promise 解决或拒绝,返回的 promise 就会解决或拒绝
let p1 = genPromise(1)
let p2 = genPromise(undefined, 2)
let p3 = genPromise(3)
Promise.race([p1, p2, p3]).then((res) => {
console.log(res) // 1
})
// 一旦某个 promise 解决或拒绝,返回的 promise 就会解决或拒绝
let p4 = genPromise(undefined, 4)
let p5 = genPromise(5)
let p6 = genPromise(undefined, 6)
Promise.race([p4, p5, p6]).then(undefined, (reason) => {
console.log(reason) // 4
})
race 原理
Promise.race = function (promises) {
return new Promise((resolve, reject) => {
if (!Array.isArray(promises)) return reject(promises + 'must be an array')
for (let p of promises) {
p.then(resolve, reject)
}
})
}
Promise.allSettled
- 返回一个在所有给定的 promise 都已经 fulfilled 或 rejected后的 promise,并带有一个对象数组,每个对象表示对应的 promise 结果
- 当有多个彼此不依赖的异步任务成功完成时,通常使用它
let genPromise = function (data, reason) {
return new Promise((resolve, reject) => {
return reason ? reject(reason) : resolve(data)
})
}
// 等待所有promise都变成完成态,返回结果数组
let p1 = genPromise(1)
let p2 = genPromise(undefined, 2)
let p3 = genPromise(3)
Promise.allSettled([p1, p2, p3]).then((res) => {
console.log(res)
/**
* 0: {state: 'fulfilled', value: 1}
* 1: {state: 'rejected', reason: 2}
* 2: {state: 'fulfilled', value: 3}
* */
})
allSettled 原理
Promise.allSettled = function (promises) {
if (!Array.isArray(promises)) return reject(promises + 'must be an array')
let res = []
let len = promises.length
for (let i = 0; i < len; i++) {
promises[i].then(
(data) => {
res[i] = {
state: 'fulfilled',
value: data,
}
},
(reason) => {
res[i] = {
state: 'rejected',
reason,
}
}
)
}
}
手写 Promise
上面的 API 大部分都已经实现了,现在看下 Promise 本身的实现,参考Promise A+规范
第一步
- Promise 实例有三个状态:Pending | Rejected | Fulfilled
- 接受一个函数作为参数,实例化时,该函数被立即调用
- 函数参数拥有两个参数:resolve & reject,这是两个方法
- 当 resolve 方法被调用时,Promise 的状态由 Pending 变为 Fulfilled
- 当 reject 方法被调用时,Promise 的状态由 Pending 变为 Rejected
class Promise {
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(excutor) {
// 用于记录Promise变为fulfilled时要传递的值
this.value = void 0
// 用于记录Promise变为Rejected时要传递的拒因
this.reason = void 0
// Promise实例的初始状态
this.state = Promise.PENDING
// 用于将promise从pending状态变为fulfilled状态的方法
let resolve = (data) => {
if (this.state === Promise.PENDING) {
this.data = data
this.state = Promise.FULFILLED
}
}
// 用于将promise从pending状态变为rejected状态的方法
let reject = (reason) => {
if (this.state === Promise.PENDING) {
this.reason = reason
this.state = Promise.REJECTED
}
}
// 构造器一经调用,函数参数就立即执行
try {
excutor(resolve, reject)
} catch (e) {
reject(e)
}
}
}
ok,第一步就已经完成了
第二步
then 方法
- then 方法接收两个函数作为参数
- 这两个参数都是可选的,非必填
- onFulFilled 函数,接受一个 value 作为 Promise 已解决状态的返回值
- onRejected 函数,接收一个 reason 作为 Promise 已拒绝状态的拒因
- 这两个函数都是异步调用的
- then 方法的返回值是一个新的 Promise 实例,与调用 then 方法的 Promise 实例不能是同一个
- 这使得 then 方法可以链式调用
- then 方法可以在一个 promise 实例上多次调用
- 被注册的回调,会在 Promise 实例状态发生改变时,依次按顺序被调用
class Promise {
constructor() {
// ... 省略其他
this.onFulfilledCb = []
this.onRejectedCb = []
// ... 省略其他
}
then(onFulfilled, onRejected) {
// 这两个参数必须是函数类型,如果不是进行兜底处理
onFulfilled =
typeof onFulfilled !== 'function' ? (value) => value : onFulfilled
onRejected =
typeof onRejected !== 'function'
? (reason) => {
throw reason
}
: onRejected
// then方法返回一个新的promise
let p2 = new Promise((resolve, reject) => {
// onFulfilled 和 onRejected 只有在执行环境堆栈仅包含平台代码时才可被调用,这里进行统一包装,并 catch 异常
let wrappedOnFulfilledCb = () => {
queueMicrotask(() => {
try {
let x = onFulfilled(this.value)
// 根据标准,如果 onFulfilled 方法返回一个值,则需要运行下面的 Promise 解决过程
resolePromise(p2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}
let wrappedOnRejectedCb = () => {
queueMicrotask(() => {
try {
let x = onRejected(this.reason)
// 根据标准,如果 onRejected 方法返回一个值,则也需要运行下面的 Promise 解决过程
resolePromise(p2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}
// 如果是 pending 状态,则把回调先暂存,等状态改变后再调用
if (this.state === Promise.PENDING) {
this.onFulfilledCb.push(wrappedOnFulfilledCb)
this.onRejectedCb.push(wrappedOnRejectedCb)
}
// 如果是 fulfilled 状态
if (this.state === Promise.FULFILLED) {
wrappedOnFulfilledCb()
}
// 如果是 rejected 状态
if (this.state === Promise.REJECTED) {
wrappedOnRejectedCb()
}
})
return p2
}
}
所以,then 方法本身是同步调用的,传给它的两个回调才是放到微任务队列异步执行的
catch 方法
class Promise {
// ...省略其他
catch(fn) {
return this.then(undefined, fn)
}
}
第三步
promise 解决过程
过程相对来说步骤比较多,不过很好理解,按照标准一步一步写就行了
- 如果 x 与 p2 是同一个对象,则返回一个 TypeError
这主要是为了避免这种情况:
let p1 = new Promise((resolve) => {
resolve(1)
})
let p2 = p1.then((value) => {
return p2
})
// TypeError: Chaining cycle detected for promise #<Promise>
- 如果 x 不为对象或者函数,以 x 为参数执行 promise
- 如果 x 是一个对象(包括 promise 对象),或者函数
- 把 x.then 赋值给 then
- 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
- 如果 then 是函数,将 x 作为函数的作用域 this 调用之,传递两个回调函数作为参数
- 第一个参数叫做 resolvePromise 第二个参数叫做 rejectPromise
- 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
- 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
- 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
- 如果调用 then 方法抛出了异常 e
- 如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之
- 否则以 e 为据因拒绝 promise
- 如果 then 不是函数,以 x 为参数执行 promise
实现
function resolvePromise(p2, x, resolve, reject) {
if (p2 === x) {
return reject(
new TypeError('Chaining cycle detected for promise #<Promise>')
)
}
if ((typeof x === 'object' || typeof x === 'function') && x !== null) {
let called = false
try {
let then = x.then
if (typeof then === 'function') {
then.call(
x,
(y) => {
if (called) return
called = true
resolvePromise(p2, y, resolve, reject)
},
(r) => {
if (called) return
called = true
reject(r)
}
)
} else {
resolve(x)
}
} catch (e) {
if (called) return
called = true
reject(e)
}
} else {
resolve(x)
}
}
完整代码
class Promise {
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(excutor) {
// 用于记录Promise变为fulfilled时要传递的值
this.value = void 0
// 用于记录Promise变为Rejected时要传递的拒因
this.reason = void 0
// Promise实例的初始状态
this.state = Promise.PENDING
// 解决多次调用 then 方法的场景
this.onFulfilledCb = []
this.onRejectedCb = []
// 使用箭头函数,绑定this指向
let resolve = (value) => {
if (this.state === Promise.PENDING) {
this.value = value
this.state = Promise.FULFILLED
this.onFulfilledCb.forEach((cb) => cb())
}
}
// 使用箭头函数,绑定this指向
let reject = (reason) => {
if (this.state === Promise.PENDING) {
this.reason = reason
this.state = Promise.REJECTED
this.onRejectedCb.forEach((cb) => cb())
}
}
// 构造器一经调用,函数参数就立即执行
try {
excutor(resolve, reject)
} catch (e) {
reject(e)
}
}
then(onFulfilled, onRejected) {
// 这两个参数必须是函数类型,如果不是进行兜底处理
onFulfilled =
typeof onFulfilled !== 'function' ? (value) => value : onFulfilled
onRejected =
typeof onRejected !== 'function'
? (reason) => {
throw reason
}
: onRejected
// then方法返回一个新的promise
let p2 = new Promise((resolve, reject) => {
// onFulfilled 和 onRejected 只有在执行环境堆栈仅包含平台代码时才可被调用,这里进行统一包装,并 catch 异常
let wrappedOnFulfilledCb = () => {
queueMicrotask(() => {
try {
let x = onFulfilled(this.value)
// 根据标准,如果 onFulfilled 方法返回一个值,则需要运行下面的 Promise 解决过程
resolvePromise(p2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}
let wrappedOnRejectedCb = () => {
queueMicrotask(() => {
try {
let x = onRejected(this.reason)
// 根据标准,如果 onRejected 方法返回一个值,则也需要运行下面的 Promise 解决过程
resolvePromise(p2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}
// 如果是 pending 状态,则把回调先暂存,等状态改变后再调用
if (this.state === Promise.PENDING) {
this.onFulfilledCb.push(wrappedOnFulfilledCb)
this.onRejectedCb.push(wrappedOnRejectedCb)
}
// 如果是 fulfilled 状态
if (this.state === Promise.FULFILLED) {
wrappedOnFulfilledCb()
}
// 如果是 rejected 状态
if (this.state === Promise.REJECTED) {
wrappedOnRejectedCb()
}
})
return p2
}
catch(fn) {
return this.then(null, fn)
}
static resolve(data) {
if (data instanceof Promise) return data
return new Promise((resolve, reject) => {
try {
if (data.then && typeof data.then === 'function') {
data.then(resolve, reject)
} else {
resolve(data)
}
} catch (error) {
reject(error)
}
})
}
static reject(reason) {
return new Promise((_, reject) => {
reject(reason)
})
}
}
function resolvePromise(p2, x, resolve, reject) {
// 整个过程就按照标准一步步实现即可
if (p2 === x) {
return reject(
new TypeError('Chaining cycle detected for promise #<Promise>')
)
}
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
let called = false
try {
let then = x.then
if (typeof then === 'function') {
then.call(
x,
(y) => {
if (called) return
called = true
resolvePromise(p2, y, resolve, reject)
},
(r) => {
if (called) return
called = true
reject(r)
}
)
} else {
resolve(x)
}
} catch (e) {
if (called) return
called = true
reject(e)
}
} else {
resolve(x)
}
}
// ======== 跑测试用例使用 ========= //
// Promise.deferred = function () {
// let dfd = {}
// dfd.promise = new Promise((resolve, reject) => {
// dfd.resolve = resolve
// dfd.reject = reject
// })
// return dfd
// }
// module.exports = Promise
测试结果
常见问题
输出顺序
第一个
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)
输出结果如下:
3
7
4
1
2
5
- first 被调用,Promise 构造器执行,先输出
3 - first 内部第二个 Promise 构造器执行,输出
7 - 遇到 setTimeout 放到 Tasks 队列,主线程继续向下执行
- resolve(1) 将 p 的状态改为 fulfilled
- resolve(2) 将外部 promise 的状态改为 fulfilled
- p.then 注册第一个微任务,first 内部逻辑执行完成
- first().then 方法注册第二个微任务
- 输出
4,此时主线程为空,开始检查微任务队列 - 两个 promise 的状态现在都是 fulfilled,首先调用 p.then 注册的微任务,输出
1 - 然后调用 first().then 注册的微任务,输出
2。微任务队列为空,转而检查 Tasks 队列 - 定时器已到时间,取出并执行,输出
5 - resolve(6) 被忽略,因为 promise 的状态已经是已完成了
第二个
这道题虽然看似简单,但其实里面的运行机制基本上涉及到了 Promise 的所有流程
let p1 = new Promise((resolve) => {
resolve(1)
})
.then((v) => {
console.log(v)
return v + 1
})
.then((v) => {
console.log(v)
return v + 1
})
.then((v) => {
console.log(v)
return v + 1
})
let p10 = new Promise((resolve) => {
resolve(10)
})
.then((v) => {
console.log(v)
return v + 1
})
.then((v) => {
console.log(v)
return v + 1
})
.then((v) => {
console.log(v)
return v + 1
})
输出结果如下:
1
10
2
11
3
12
交替输出,你能解释清楚这其中的原因吗?
- p1 构造器执行,p1 立即 resolve,变为 fulfilled
- p1.then 的调用会将注册的回调放入到微任务队列;then 方法执行返回一个新的 promise,我们叫它 p2(依次类推 p3、p4)
- 然后 p2、p3、p4 的 then 方法也会依次执行(因为 then 方法是同步调用),但是此时的 p2、p3、p4 还都是 pending 状态,所以它们的回调会被放到各自的 callback 数组里暂存起来而不是微任务队列里。这里可以结合代码来看
let wrappedOnFulfilledCb = () => {
queueMicrotask(() => {
try {
let x = onFulfilled(this.value)
// 根据标准,如果 onFulfilled 方法返回一个值,则需要运行下面的 Promise 解决过程
resolvePromise(p2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}
// 如果是 pending 状态,则把回调先暂存,等状态改变后再调用
if (this.state === Promise.PENDING) {
this.onFulfilledCb.push(wrappedOnFulfilledCb)
}
可以看到 pending 状态下,各自维护的 callback 数组中存储的是一个被 wrap 后的回调,将真正的用户传入的回调推入微任务队列的时机则是在这个 wrap 后的回调被执行之后
那这个被 wrap 的回调被执行的地方是在哪里呢?咱们以 wrappedOnFulfilledCb 这个回调来看,它被调用的时机有两个
// 1、是在then方法被调用时 且 Promise的状态不为pending的时候
if (this.state === Promise.FULFILLED) {
wrappedOnFulfilledCb()
}
// 2、就是在 resolve 方法里,这种情况下,当resolve被调用的时候,才会真正的将用户的回调放到微任务队列中去
let resolve = (value) => {
if (this.state === Promise.PENDING) {
this.value = value
this.state = Promise.FULFILLED
// 这里执行的 callback 其实只是被 wrap 后的回调,也就是上面的 wrappedOnFulfilledCb
// 它执行的结果就是 将用户真正的回调放到微任务队列里
this.onFulfilledCb.forEach((cb) => cb())
}
}
ok,继续
- p10 的流程同上
- 主线程任务执行完毕,检查微任务队列,此时微任务队列中仅有 p1 和 p10,依次拿出他们注册的回调并执行,分别输出 1 和 10
- p1 和 p10 的回调执行的过程中,又会将 p2 和 p11 的状态置为 fulfilled,并将他们的 then 回调放到微任务队中去
- 这一步则是在 resolvePromise 的过程中,调用 resolve 方法时做到的
- 然后重复上面的步骤,直到所有微任务被清空
所以最终执行的结果就是交替输出的
每隔 1 秒输出 1,2,3
使用 promise 实现,每隔一秒输出 1,2,3
;[1, 2, 3].reduce((p, x) => {
return p.then((res) => {
return new Promise((resolve) => {
setTimeout(() => {
console.log(x)
resolve()
}, 1000)
})
})
}, Promise.resolve())
红绿灯交替闪烁
红灯 3 秒亮一次,黄灯 2 秒亮一次,绿灯 1 秒亮一次;如何让三个灯不断交替、循环亮灯
三个亮灯函数:
function red() {
console.log('red')
}
function green() {
console.log('green')
}
function yellow() {
console.log('yellow')
}
实现
- 利用递归实现不断重复的效果
let sleep = (time, fn) =>
new Promise((resolve) => {
setTimeout(() => {
fn()
resolve()
}, time * 1000)
})
let step = () => {
return Promise.resolve()
.then(() => {
return sleep(3, red)
})
.then(() => {
return sleep(2, green)
})
.then(() => {
return sleep(1, yellow)
})
.then(() => {
step() // 使用递归用来实现不断重复的效果
})
}
step()
实现异步并发控制
/**
* 异步并发限制
*
* @export
* @param {Array} sources
* @param {*} callback
* @param {*} limit
* @returns
*/
async function limitAsyncConcurrency(sources, callback, limit = 5) {
let done
let lock = []
let results = []
let runningCount = 0
let total = sources.length
if (!total) return
const p = new Promise((resolve) => (done = resolve))
const block = async () => {
return new Promise((resolve) => lock.push(resolve))
}
// 解除lock
const next = () => {
const fn = lock.shift()
fn && fn()
runningCount--
}
const excutor = async (index, item) => {
// 限制并发
if (runningCount >= limit) await block()
runningCount++
new Promise((resolve, reject) =>
callback(index, item, resolve, reject)
).then((res) => {
total--
next()
results[index] = res
if (!total) {
done(results)
}
})
}
for (const [index, item] of sources.entries()) {
excutor(index, item)
}
return p
}
let sources = ['1.text', '2.txt', '3.txt', '4.txt', '5.txt', '6.txt', '7.txt']
let getFile = (index, file, resolve, reject) => {
setTimeout(() => {
console.log(index, file)
resolve(file)
}, 1000)
}
imitAsyncConcurrency(sources, getFile, 3).then((res) => {
console.log(res)
})
输出结果如下:
0 '1.text'
1 '2.txt'
2 '3.txt'
// 间隔 1s
3 '4.txt'
4 '5.txt'
5 '6.txt'
// 间隔 1s
6 '7.txt'
// 最后输出
['1.text', '2.txt', '3.txt', '4.txt', '5.txt', '6.txt', '7.txt']