-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
setTimeout 链式调用 #49
Comments
顺带着看了下 PromiseA+ 规范,写了如下的 Promise: class _Promise {
static PENDING = 'pending'
static RESOLVED = 'resolved'
static REJECTED = 'rejected'
static resolve (value) {
if (!value) return new _Promise(res => { res() })
if (value instanceof _Promise) return value
return new _Promise(resolve => resolve(value))
}
static reject (reason) {
return new _Promise((resolve, reject) => reject(reason))
}
constructor (executor) {
// initialize
this.state = _Promise.PENDING
// 成功的值
this.value = undefined
// 失败的原因
this.reason = undefined
// 支持异步
this.resolveQueue = []
this.rejectQueue = []
// success
let resolve = (value) => {
if (this.state === _Promise.PENDING) {
this.state = _Promise.RESOLVED
this.value = value
this.resolveQueue.forEach(cb => cb(value))
}
}
// failure
let reject = (reason) => {
if (this.state === _Promise.PENDING) {
this.state = _Promise.REJECTED
this.reason = reason
this.rejectQueue.forEach(cb => cb(reason))
}
}
// immediate execute
try {
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
then (onResolved, onRejected) {
return new _Promise((resolve, reject) => {
const resolvePromise = res => {
try {
if (typeof onResolved !== 'function') {
resolve(res)
} else {
const value = onResolved(res)
value instanceof _Promise ? value.then(resolve, reject) : resolve(value)
}
} catch (err) {
reject(err)
}
}
const rejectPromise = err => {
try {
if (typeof onRejected !== 'function') {
reject(err)
} else {
const value = onRejected(err)
value instanceof _Promise ? value.then(resolve, reject) : reject(value)
}
} catch (error) {
reject(error)
}
}
if (this.state === _Promise.RESOLVED) {
setTimeout(() => resolvePromise(this.value), 0)
}
if (this.state === _Promise.REJECTED) {
setTimeout(() => rejectPromise(this.reason), 0)
}
if (this.state === _Promise.PENDING) {
if (onResolved && typeof onResolved === 'function') {
this.resolveQueue.push(() => {
setTimeout(() => resolvePromise(this.value), 0)
})
}
if (onRejected && typeof onRejected === 'function') {
this.rejectQueue.push(() => {
setTimeout(() => rejectPromise(this.reason), 0)
})
}
}
})
}
catch (onRejected) {
return this.then(null, onRejected)
}
finally (callback) {
return this.then(value => {
return _Promise.resolve(callback()).then(() => value)
}, reason => {
return _Promise.resolve(callback()).then(() => { throw reason })
})
}
} 这里用 setTimeout 模拟了微任务 then 方法,写了下面的测试例子: setTimeout(() => {
console.log('after then')
}, 0)
new _Promise((resolve) => {
console.log('new')
resolve('then')
}).then(val => console.log(val))
console.log('after new')
// 打印顺序是
// new
// after new
// after then 因为是用定时器模拟的,所以如果定时器时间为 0 的话,定时器先于 then 方法执行
// then 为了更真实地模拟微任务,可以使用 scope.queueMicrotask(function) 代替 setTimeout(fn, 0)。 这里只是模拟的 Promise,在真实的 Promise 里,遇到如下 case: const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('foo')
}, 7000)
})
myPromise
.then(value => { return value + ' and bar'; })
.then(value => { return value + ' and bar again'; })
.then(value => { return value + ' and again'; })
.then(value => { return value + ' and again'; })
.then(value => { console.log(value) })
.then(() => {
throw 'custom error'
})
.finally(() => {
console.log(1111)
})
.catch(err => { console.log(err) })
.then(() => {
console.log(2222)
})
// 打印顺序为
// foo and bar and bar again and again and again
// 1111
// custom error
// 2222 在 finally 之后的 catch 和 then 方法都执行了,但是在 _Promise 中 finally 之后的代码都没有被执行,说明自己实现的 _Promise 还是存在问题的。 打印了一下 myPromise 和 t,发现 myPromise 最后结果是 resolved 的 promise 对象,而 t 还处于 pending 状态,问题就出在这里。 myPromise Promise { 'foo' }
_Promise {
state: 'pending',
value: undefined,
reason: undefined,
resolveQueue: [],
rejectQueue: []
} |
目前还是和原生 Promise 存在不一致的问题,比如下面的 6 就不会被打印,原生 Promise 是会打印的。 let sleep = function (time = 0) {
return new _Promise(resolve => setTimeout(resolve, time))
}
let start = Date.now(), end = 0
let t = sleep(1000).then(() => {
console.log(1)
return sleep(2000)
}).then(() => {
console.log(2)
return sleep(3000)
}).then(() => {
console.log(3)
end = Date.now()
console.log('total ms', end - start)
}).then(() => {
throw 'custom error'
}).catch(e => {
console.log('e1 =', e)
}).finally(() => {
console.log(4)
}).then(() => {
console.log(5)
}).catch(e => {
console.log('e2 =', e)
}).then(() => {
console.log(6)
})
// 原生 promise 会打印 6,但是自己实现的不会打印 |
参照了 promise 库的写法,完善了循环调用和状态只能改变一次,现在的 Promise 执行就符合预期了。 class _Promise {
static PENDING = 'pending'
static RESOLVED = 'resolved'
static REJECTED = 'rejected'
static resolve (value) {
if (value instanceof _Promise) return value
return new _Promise(resolve => resolve(value))
}
static reject (reason) {
return new _Promise((_, reject) => reject(reason))
}
constructor (executor) {
// initialize
this.state = _Promise.PENDING
// 成功的值
this.value = undefined
// 失败的原因
this.reason = undefined
// 支持异步
this.resolveQueue = []
this.rejectQueue = []
// success
let resolve = (value) => {
if (this.state === _Promise.PENDING) {
this.state = _Promise.RESOLVED
this.value = value
this.resolveQueue.forEach(cb => cb(value))
}
}
// failure
let reject = (reason) => {
if (this.state === _Promise.PENDING) {
this.state = _Promise.REJECTED
this.reason = reason
this.rejectQueue.forEach(cb => cb(reason))
}
}
// immediate execute
try {
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
then (onResolved, onRejected) {
// 对 onResolved,onRejected 作规范化处理,统一转换为函数
onResolved = typeof onResolved === 'function' ? onResolved : value => value
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
/**
* 用于处理循环调用,保证同一时刻只能调用一次 resolve 或 reject
* @param {*} promise2
* @param {*} x
* @param {*} resolve
* @param {*} reject
* @returns
*/
const resolvePromise = (promise2, x, resolve, reject) => {
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
let called
if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
try {
let then = x.then
if (typeof then === 'function') {
then.call(x, y => {
if (called) return
called = true
resolvePromise(promise2, 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) // 普通值直接 resolve
}
}
let promise2 = new _Promise((resolve, reject) => {
const handleResolved = () => {
try {
const x = onResolved(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}
const handleRejected = () => {
try {
const x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}
if (this.state === _Promise.RESOLVED) {
queueMicrotask(handleResolved)
}
if (this.state === _Promise.REJECTED) {
queueMicrotask(handleRejected)
}
if (this.state === _Promise.PENDING) {
this.resolveQueue.push(() => queueMicrotask(handleResolved))
this.rejectQueue.push(() => queueMicrotask(handleRejected))
}
})
return promise2
}
catch (onRejected) {
return this.then(undefined, onRejected)
}
finally (callback = () => {}) {
return this.then(value => {
return _Promise.resolve(callback()).then(() => value)
}, reason => {
return _Promise.resolve(callback()).then(() => { throw reason })
})
}
} |
有这样一段代码:
这段代码将会在 1s、3s 和 6s 时分别打印 1、2、3。当定时器过多时,这种嵌套会导致代码臃肿。为了解决这个问题,思路就是如何实现定时器链式调用。
提起链式调用就会想到 ES6 中的 Promise.then,所以问题就是怎么把每个定时器转换为 Promise。默认情况下,每一个 .then() 方法还会返回一个新生成的 promise 对象,这个对象可被用作链式调用。
将一个定时器转换为 Promise 其实就是常见的 sleep 方法:
接下来要做的事情就是自定义 .then 方法的返回值,为了实现定时器链式调用需要直接返回 sleep。
The text was updated successfully, but these errors were encountered: