三种状态:等待中(pending)完成了 (resolved)拒绝了(rejected)
2 个过程:pending -> resolved pending -> rejected
Promise 实现了链式调用,也就是说每次调用 then 之后返回的都是一个 Promise,并且是一个全新的 Promise,原因也是因为状态不可变。
如果在 then 中 使用了 return,那么 return 的值会被 Promise.resolve() 包装
优点:Promise 也很好地解决了回调地狱的问题
缺点:无法取消 Promise,错误需要通过回调函数捕获
手写 Promise
func(params, cb) --> promise(resolve, reject).then(res => {}).catch(err => {}).finally(() => {}) --> async await
观察者模式: 收集依赖 -> 触发通知 -> 取出依赖执行
在Promise里,执行顺序是then收集依赖 -> 异步触发resolve -> resolve执行依赖
//Promise/A+规定的三种状态
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
// 构造方法接收一个回调
constructor(executor) {
this._status = PENDING // Promise状态
this._value = undefined // 储存then回调return的值
this._resolveQueue = [] // 成功队列, resolve时触发
this._rejectQueue = [] // 失败队列, reject时触发
// 由于resolve/reject是在executor内部被调用, 因此需要使用箭头函数固定this指向, 否则找不到this._resolveQueue
let _resolve = (val) => {
//把resolve执行回调的操作封装成一个函数,放进setTimeout里,以兼容executor是同步代码的情况
const run = () => {
if(this._status !== PENDING) return // 对应规范中的"状态只能由pending到fulfilled或rejected"
this._status = FULFILLED // 变更状态
this._value = val // 储存当前value
// 这里之所以使用一个队列来储存回调,是为了实现规范要求的 "then 方法可以被同一个 promise 调用多次"
// 如果使用一个变量而非队列来储存回调,那么即使多次p1.then()也只会执行一次回调
while(this._resolveQueue.length) {
const callback = this._resolveQueue.shift()
callback(val)
}
}
setTimeout(run)
}
// 实现同resolve
let _reject = (val) => {
const run = () => {
if(this._status !== PENDING) return // 对应规范中的"状态只能由pending到fulfilled或rejected"
this._status = REJECTED // 变更状态
this._value = val // 储存当前value
while(this._rejectQueue.length) {
const callback = this._rejectQueue.shift()
callback(val)
}
}
setTimeout(run)
}
// new Promise()时立即执行executor,并传入resolve和reject
executor(_resolve, _reject)
}
// then方法,接收一个成功的回调和一个失败的回调
then(resolveFn, rejectFn) {
// 根据规范,如果then的参数不是function,则我们需要忽略它, 让链式调用继续往下执行
typeof resolveFn !== 'function' ? resolveFn = value => value : null
typeof rejectFn !== 'function' ? rejectFn = reason => {
throw new Error(reason instanceof Error? reason.message:reason);
} : null
// return一个新的promise
return new MyPromise((resolve, reject) => {
// 把resolveFn重新包装一下,再push进resolve执行队列,这是为了能够获取回调的返回值进行分类讨论
const fulfilledFn = value => {
try {
// 执行第一个(当前的)Promise的成功回调,并获取返回值
let x = resolveFn(value)
// 分类讨论返回值,如果是Promise,那么等待Promise状态变更,否则直接resolve
x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
} catch (error) {
reject(error)
}
}
// reject同理
const rejectedFn = error => {
try {
let x = rejectFn(error)
x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
} catch (error) {
reject(error)
}
}
switch (this._status) {
// 当状态为pending时,把then回调push进resolve/reject执行队列,等待执行
case PENDING:
this._resolveQueue.push(fulfilledFn)
this._rejectQueue.push(rejectedFn)
break;
// 当状态已经变为resolve/reject时,直接执行then回调
case FULFILLED:
fulfilledFn(this._value) // this._value是上一个then回调return的值(见完整版代码)
break;
case REJECTED:
rejectedFn(this._value)
break;
}
})
}
//catch方法其实就是执行一下then的第二个回调
catch(rejectFn) {
return this.then(undefined, rejectFn)
}
//finally方法
finally(callback) {
return this.then(
value => MyPromise.resolve(callback()).then(() => value), //执行回调,并returnvalue传递给后面的then
reason => MyPromise.resolve(callback()).then(() => { throw reason }) //reject同理
)
}
//静态的resolve方法
static resolve(value) {
if(value instanceof MyPromise) return value //根据规范, 如果参数是Promise实例, 直接return这个实例
return new MyPromise(resolve => resolve(value))
}
//静态的reject方法
static reject(reason) {
return new MyPromise((resolve, reject) => reject(reason))
}
//静态的all方法
static all(promiseArr) {
let index = 0
let result = []
return new MyPromise((resolve, reject) => {
promiseArr.forEach((p, i) => {
//Promise.resolve(p)用于处理传入值不为Promise的情况
MyPromise.resolve(p).then(
val => {
index++
result[i] = val
if(index === promiseArr.length) {
resolve(result)
}
},
err => {
reject(err)
}
)
})
})
}
//静态的race方法
static race(promiseArr) {
return new MyPromise((resolve, reject) => {
//同时执行Promise,如果有一个Promise的状态发生改变,就变更新MyPromise的状态
for (let p of promiseArr) {
MyPromise.resolve(p).then( //Promise.resolve(p)用于处理传入值不为Promise的情况
value => {
resolve(value) //注意这个resolve是上边new MyPromise的
},
err => {
reject(err)
}
)
}
})
}
}
实现 Promise.all()
实现 Promise.race()
Scheduler 请求数量调度器
题目描述: JS 实现一个带并发限制的异步调度器 Scheduler,保证同时运行的任务最多有两个
addTask(1000,"1");
addTask(500,"2");
addTask(300,"3");
addTask(400,"4");
的输出顺序是:2 3 1 4
整个的完整执行流程:
一开始1、2两个任务开始执行
500ms时,2任务执行完毕,输出2,任务3开始执行
800ms时,3任务执行完毕,输出3,任务4开始执行
1000ms时,1任务执行完毕,输出1,此时只剩下4任务在执行
1200ms时,4任务执行完毕,输出4
实现代码如下:
class Scheduler {
constructor(limit) {
this.maxCount = limit;
this.runCounts = 0;
this.queue = [];
}
add(time, order) {
const p = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(order);
resolve();
}, time);
});
};
this.queue.push(p);
}
taskStart() {
const request = () => {
if (!this.queue || !this.queue.length || this.runCounts >= this.maxCount) {
return;
}
this.runCounts++;
const p = this.queue.shift()
p().then(() => {
this.runCounts--;
// 执行完继续递归判断
this.request();
});
}
for (let i = 0; i < this.maxCount; i++) {
request();
}
}
}
const scheduler = new Scheduler(2);
const addTask = (time, order) => {
scheduler.add(time, order);
};
addTask(1000, "1");
addTask(500, "2");
addTask(300, "3");
addTask(400, "4");
scheduler.taskStart();
limit 请求数限制
/**
* 关键点
* 1. new promise 一经创建,立即执行
* 2. 使用 Promise.resolve().then 可以把任务加到微任务队列,防止立即执行迭代方法
* 3. 微任务处理过程中,产生的新的微任务,会在同一事件循环内,追加到微任务队列里
* 4. 使用 race 在某个任务完成时,继续添加任务,保持任务按照最大并发数进行执行
* 5. 任务完成后,需要从 doingTasks 中移出
*/
function limit(count, array, iterateFunc) {
const tasks = []
const doingTasks = []
let i = 0
const enqueue = () => {
if (i === array.length) {
return Promise.resolve()
}
const task = Promise.resolve().then(() => iterateFunc(array[i++]))
tasks.push(task)
const doing = task.then(() => doingTasks.splice(doingTasks.indexOf(doing), 1))
doingTasks.push(doing)
const res = doingTasks.length >= count ? Promise.race(doingTasks) : Promise.resolve()
return res.then(enqueue)
};
return enqueue().then(() => Promise.all(tasks))
}
// test
const timeout = i => new Promise(resolve => setTimeout(() => resolve(i), i))
limit(2, [1000, 1000, 1000, 1000], timeout).then((res) => {
console.log(res)
})
arrange进行时间和工作调度
function arrange (name) {
const task = [];
task.push(() => {
console.log(`${name} is notified`);
})
async function execute () {
for (callback of task) {
await callback();
}
}
function doSomething (method) {
task.push(() => {
console.log(`Start to ${method}`);
})
return this;
}
function wait (time) {
task.push(() => {
new Promise((resolve, reject) => {
console.log(`等待${time}秒`);
setTimeout(resolve, time);
})
})
return this;
}
function waitFirst (time) {
task.unshift(() => {
new Promise((resolve, reject) => {
console.log(`等待${time}秒`)
setTimeout(resolve, time);
})
})
return this;
}
return {
execute,
do: doSomething,
wait,
waitFirst
}
}
// arrange('William').execute();
// arrange('William').do('commit').execute();
// arrange('William').wait(5).do('commit').execute();
arrange("William").waitFirst(5).do("push").execute();
取消promise 手动
function myCancelPromiseWrap (p) {
let cancel;
let p1 = new Promise((resolve, reject) => {
cancel = () => reject('取消');
})
let p2 = Promise.race([p1, p])
p2.cancel = cancel
return p2
}
let p = myCancelPromiseWrap(new Promise((resolve, reject) => {
setTimeout(() => {
resolve(123)
}, 5000)
}))
p.cancel();
取消promise promise.race
function fn1() {//模仿一个异步函数,此函数一秒以后执行完,返回成功的promise
return new Promise((resolve) => {
setTimeout(() => {
resolve(777)
}, 1000)
})
}
function timerCtrl(time) {
return new Promise((resolve) => {
setTimeout(() => {
resolve("超时") //如果在秒数要求到达还未执行完
}, time)
})
}
function timerCancelPromise(fn1, time) {//传入要监听的函数和秒数要求
return new Promise((resolve, reject) => {
Promise.race([timerCtrl(time), fn1()]).then((result) => {
if (result == '超时') {
reject("超时");
} else {
resolve(result);
}
})
})
}
timerCancelPromise(fn1, 2000).then((res) => {
console.log("成功", res)
}, (err) => {
console.log("失败", err)
})
取消promise setTimeout
function fn1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, 1000)
})
}
function timerOut (fn, timer) {
return new Promise((resolve, reject) => {
fn.then(() => {
resolve();
})
setTimeout(() => {
reject();
}, timer)
})
}
timerOut(fn1, 2000).then(() => {
console.log('成功');
}).catch(() => {
console.log('超时');
})
timeSleep
async function timeSleep(delay) {
return await new Promise((resolve, reject) => {
setTimeout(() => {
resolve("执行完毕")
}, delay);
})
}
每隔3s打印3
function sleep(timeout) {
return new Promise((resolve, reject) => {
setTimeout(resolve, timeout)
})
}
async function myConsole() {
for (let i = 1; ; i++) {
if (i % 3 === 0) {
await sleep(3000)
console.log(3);
} else {
await sleep(i % 3 * 1000)
console.log(i % 3);
}
}
}
myConsole();
红绿黄灯循环
const lightTask = (light, timer) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (light === 'red') red();
if (light === 'yellow') yellow();
if (light === 'green') green();
resolve();
}, timer)
})
}
const step = () => {
lightTask('red', 3000)
.then(() => lightTask('green', 1000))
.then(() => lightTask('yellow', 2000))
.then(step);
}
step();
异步 asyncAdd 写 sum
// 提供一个异步 add 方法如下,需要实现一个 async sum(...nums) 函数:
function asyncAdd(a, b, callback) {
setTimeout(function() {
callback(null, a + b)
}, 1000)
}
// 异步用 promise 包一下 再用 async await 就能同步执行了
function promiseAdd (num1, num2) {
return new Promise((resolve, reject) => {
asyncAdd(num1, num2, (err, rs) => {
if (err) {
reject(err);
} else {
resolve(rs);
}
})
})
}
async function sum(...nums) {
let res = 0;
for (const n of nums) {
res = await promiseAdd(res, n);
}
return res;
}
// ================ 测试 ================
sum(1,2,3,4,5,6,7,8,9,10,11).then(total => console.log(total)) // 66
// or
async function test() {
console.log(await sum(1,2,3,4,5,6,7,8,9,10,11))
}
test() // 66
// ================ 串行 执行 ================
async function serialSum(...nums) {
return nums.reduce((task, cur) => {
return task.then(res => promiseAdd(res, cur));
}, Promise.resolve(0))
}
// ================ 并行 执行 ================
async function parallelSum(...nums) {
if (nums.length === 1) return nums[0];
const tasks = [];
for (let i = 0; i < nums.length; i += 2) {
tasks.push(promiseAdd(nums[i], nums[i + 1] || 0))
}
const results = await Promise.all(tasks)
return parallelSum(...results);
}
async/await
实际上是对 Generator(生成器)的封装,是一个语法糖
差异:
- async/await自带执行器,不需要手动调用next()就能自动执行下一步
- async函数返回值是Promise对象,而Generator返回的是生成器对象
- await能够返回Promise的resolve/reject的值
优点: 相比直接使用 Promise 来说,优势在于处理 then 的调用链,能够更清晰准确的写出代码
缺点:因为 await 将异步代码改造成了同步代码,如果多个异步代码没有依赖性却使用了 await 会导致性能上的降低
regeneratorRuntime.mark 和 regeneratorRuntime.wrap,这两者其实是 regenerator-runtime 模块里的两个方法,regenerator-runtime 模块来自 facebook 的 regenerator 模块, runtime.js
Generator 实现的核心在于上下文的保存,函数并没有真的被挂起,每一次yield,其实都执行了一遍传入的生成器函数,只是在这个过程中间用了一个context对象储存上下文,使得每次执行生成器函数的时候,都可以从上一个执行结果开始执行,看起来就像函数被挂起了一样