Promise
Promise
const PromisePending = 'PENDING'
const PromiseFulfilled = 'FULFILLED'
const PromiseRejected = 'REJECTED'
class MyPromise {
constructor(fn) {
if (typeof fn !== 'function') {
return new TypeError(`Promise resolver ${fn} is not a function`)
}
this.PromiseState = PromisePending
this.PromiseResult = null
this.onFulfilledCallbacks = []
this.onRejectedCallbacks = []
const resolve = (value) => {
this.PromiseResult = value
this.PromiseState = PromiseFulfilled
this.onFulfilledCallbacks.forEach(fn => fn())
}
const reject = (reason) => {
this.PromiseResult = reason
this.PromiseState = PromiseRejected
this.onRejectedCallbacks.forEach(fn => fn())
}
try {
fn(resolve, reject)
} catch (error) {
reject(error)
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
const promise2 = new MyPromise((resolve, reject) => {
if (this.PromiseState === PromisePending) {
this.onFulfilledCallbacks.push(() => {
process.nextTick(() => {
try {
const x = onFulfilled(this.PromiseResult)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
})
this.onRejectedCallbacks.push(() => {
process.nextTick(() => {
try {
const x = onRejected(this.PromiseResult)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
})
}
if (this.PromiseState === PromiseFulfilled) {
process.nextTick(() => {
try {
const x = onFulfilled(this.PromiseResult)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}
if (this.PromiseState === PromiseRejected) {
process.nextTick(() => {
try {
const x = onRejected(this.PromiseResult)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}
})
return promise2
}
catch(onRejected) {
return this.then(undefined, onRejected)
}
finally(callback) {
return this.then(callback, callback)
}
}
const resolvePromise = (promise2, x, resolve, reject) => {
if (promise2 === x)
return reject(new Error('Chaining cycle detected for promise #<Promise>"'))
if (x && (typeof x === 'object' || typeof x === 'function')) {
let called = false
try {
const then = x.then
if (typeof then !== 'function') {
resolve(x)
} else {
then.call(x,
value => {
if (called) return
called = true
resolvePromise(promise2, value, resolve, reject)
},
reason => {
if (called) return
called = true
reject(reason)
}
)
}
} catch (e) {
if (called) return
called = true
reject(e)
}
} else {
resolve(x)
}
}
MyPromise.resolve = (value) => {
return new MyPromise((resolve, reject) => {
if (value in MyPromise) {
return value
} else if (typeof value === 'object' && 'then' in value) {
return value.then(resolve, reject)
} else {
return new MyPromise(resolve => resolve(value))
}
})
}
MyPromise.reject = (reason) => {
return new MyPromise((resolve, reject) => reject(reason))
}
MyPromise.all = (promises) => {
return new MyPromise((resolve, reject) => {
if (!Array.isArray(promises))
return reject(new TypeError('Arguments is not iterator'))
let n = promises.length
if (!n)
resolve(promises)
let cnt = 0
const res = new Array(n)
promises.forEach((promise, index) => {
MyPromise.resolve(promise).then(
value => {
cnt++
res[index] = value
cnt === n && resolve(res)
},
reason => {
reject(reason)
}
)
})
})
}
MyPromise.race = (promises) => {
return new MyPromise((resolve, reject) => {
if (!Array.isArray(promises))
return reject(new TypeError('Arguments is not iterator'))
promises.forEach(promise => {
MyPromise.resolve(promise).then(resolve, reject)
})
})
}
MyPromise.allSettled = (promises) => {
return new MyPromise((resolve, reject) => {
if (!Array.isArray(promises))
return reject(new TypeError('Arguments is not iterator'))
let n = promises.length
if (!n)
resolve(promises)
let cnt = 0
const res = new Array(n)
promises.forEach((promise, index) => {
MyPromise.resolve(promise).then(
value => {
cnt++
res[index] = {
status: 'fulfilled',
value
}
cnt === n && resolve(res)
},
reason => {
cnt++
res[ind] = {
status: 'rejected',
reason
}
cnt === n && resolve(res)
}
)
})
})
}
MyPromise.any = (promises) => {
return new MyPromise((resolve, reject) => {
if (!Array.isArray(promises))
return reject(new TypeError('Arguments is not iterator'))
const n = promises.length
if (!n)
return reject(new AggregateError('All promises were iterator'))
let cnt = 0
const errors = new Array(n)
promises.forEach((promise, index) => {
MyPromise.resolve(promise).then(
value => {
resolve(value)
},
reason => {
cnt++
errors[index] = reason
cnt === n && reject(new AggregateError(errors))
}
)
})
})
}
并行限制 Promise
1. 类方法
class Scheduler {
constructor(promises, limit) {
this.queue = promises
this.limit = limit
this.runCount = 0
this.len = promises.length
this.result = new Array(this.len)
this.index = 0
}
start() {
for (let i = 0; i < this.limit; i++) {
this.request()
}
}
request() {
if (!this.queue.length || this.runCount >= this.limit) return
const task = this.queue.shift()
this.runCount++
task().then((res) => {
this.result[this.index++] = res
if (this.index === this.len) console.log(this.result)
this.runCount--
this.request()
})
}
}
2. 函数方法
function MyPromise(promises, limit) {
return new Promise(resolve => {
let runCount = 0
const len = promises.length
const result = new Array(len)
let index = 0
function request() {
if (!promises || !promises.length || runCount >= limit) return
const task = promises.shift()
runCount++
task().then(res => {
result[index++] = res
if (index === len) resolve(result)
runCount--
request()
})
}
function start() {
for (let i = 0; i < limit; i++) {
request()
}
}
start()
})
}
测试用例
const promise1 = (content, timeout) => {
return () => {
return new Promise(res => {
setTimeout(() => {
console.log(content)
console.log(Date.now() - now)
res(content)
}, timeout)
})
}
}
const promises = [
promise1(1, 3000),
promise1(2, 2000),
promise1(3, 1000),
promise1(4, 5000),
promise1(5, 2000),
promise1(6, 4000),
promise1(7, 6000),
promise1(8, 3000),
]
let now = Date.now()
const scheduler = new Scheduler(promises, 3)
scheduler.start()
MyPromise(promises, 3).then(res => console.log(res))
顺序执行 Promise
const promise1 = (content, timeout) => {
return () => {
return new Promise(res => {
setTimeout(() => {
console.log(content)
console.log(Date.now() - now)
res(content)
}, timeout)
})
}
}
const promises = [
promise1(1, 3000),
promise1(2, 2000),
promise1(3, 1000),
promise1(4, 2000),
promise1(5, 2000),
promise1(6, 1000),
promise1(7, 1000),
promise1(8, 500),
]
let now = Date.now()
Promise.resolve
function orderPromise(promises) {
let queue = Promise.resolve()
const data = []
promises.forEach(promise => {
queue = queue.then(promise).then(res => {
data.push(res)
return data
})
})
return queue
}
orderPromise(promises).then(res => {
console.log(res)
})
async function orderPromise1(promises) {
let index = 0
const res = new Array(promises.length)
while (index < promises.length) {
res[index] = await promises[index]()
index++
}
return res
}
(async () => {
const res = await orderPromise1(promises)
console.log(res)
})()
递归
function orderPromise2(promises, index = 0) {
if (index < promises.length) {
promises[index]().then(() => {
orderPromise2(promises, index + 1)
})
}
}
orderPromise2(promises, 0)
LazyMan
class LazyMan {
constructor() {
this.tasks = []
const task = () => {
console.log('开始')
this.next()
}
this.tasks.push(task)
setTimeout(() => {
this.next()
})
}
next() {
const task = this.tasks.shift()
task && task()
}
sleep(delay) {
this.sleepWrapper(delay, false)
return this
}
sleepFirst(delay) {
this.sleepWrapper(delay, true)
return this
}
sleepWrapper(delay, isFirst) {
const task = () => {
console.log('我要睡觉')
setTimeout(() => {
this.next()
}, delay)
}
if (isFirst) {
this.tasks.unshift(task)
} else {
this.tasks.push(task)
}
}
addTask(fn) {
const task = () => {
fn()
this.next()
}
this.tasks.push(task)
return this
}
}
const man = new LazyMan()
const eat = () => console.log('eat')
const run = () => console.log('run')
const study = () => console.log('study')
man.addTask(eat).sleep(2000).addTask(run).sleep(1000).addTask(study).sleepFirst(2000)
AJAX
const xhr = new XMLHttpRequest()
xhr.open('get', '/', true)
xhr.setResponseHeader('Accept', 'application/json')
xhr.responseType = 'json'
xhr.onreadystatechange = function() {
if (this.readyState === 4) {
if (this.status === 200) {
return this.responseText
} else {
throw new Error(this.responseText)
}
}
}
xhr.send(null)
function promiseAjax(method, url, isAsync = true) {
return new Promise((res, rej) => {
const xhr = new XMLHttpRequest()
xhr.open(method, url, isAsync)
xhr.setRequestHeader('Content-Type', 'application/json')
xhr.onreadystatechange = function() {
if (this.readyState === 4) {
if (this.status === 200) {
res(this.responseText)
} else {
rej(new Error(this.responeText))
}
}
}
xhr.open(null)
})
}
JSONP
function jsonp(url, params, cb) {
const script = document.createElement('script')
const query = { ...params, cb }
const arr = []
for (const param of query) {
arr.push(`${param}=${query[param]}`)
}
script.src = `${url}?${arr.join('&')}`
ducument.body.append(script)
window[cb] = function(data) {
console.log(data)
document.body.removeChild(script)
}
}
红绿灯
function red() { console.log('red') }
function yellow() { console.log('yellow') }
function green() { console.log('green') }
function task(color, timeout, callback) {
setTimeout(() => {
if (color === 'red') {
red()
} else if (color === 'yellow') {
yellow()
} else {
green()
}
callback()
}, timeout)
}
function loop() {
task('red', 1000, () => {
task('yellow', 1000, () => {
task('red', 1000, loop)
})
})
}
function task1(color, timeout) {
return new Promise(res => {
setTimeout(() => {
if (color === 'red') {
red()
} else if (color === 'yellow') {
yellow()
} else {
green()
}
res()
}, timeout)
})
}
const loop1 = () => {
task1('red', 1000)
.then(() => task1('yellow', 1000))
.then(() => task1('green', 1000))
.then(loop1)
}
const loop2 = async () => {
await task1('red', 1000)
await task1('yellow', 1000)
await task1('green', 1000)
loop2()
}
loop()
一秒打印一个数字
function print(cnt) {
setTimeout(() => {
console.log(cnt)
print(cnt + 1)
}, 1000)
}
for (var i = 1; i < 4; i++) {
setTimeout((j) => {
console.log(j)
}, i * 1000, i)
}
for (var i = 1; i < 4; i++) {
((j) => {
setTimeout(() => {
console.log(j)
}, i * 1000)
})(i)
}