js 异步
EventLoop
如何理解 JS 的异步
JS 运行在浏览器的渲染主线程中,而渲染主线程只有一个。它承担着很多工作,比如渲染页面、执行 JS 等。如果它采用同步的方式,那么执行类似于网络请求的任务时会一直等待响应,那就很有可能导致消息队列中的其他任务无法得到执行。这样的阻塞就会造成页面无法及时更新而卡死。 所以浏览器采用异步的方式,当某些任务发生时,比如计时器、网络、事件监听,主线程将任务交给其他线程去处理,自身立即结束任务的执行,转而执行后续代码。当其它线程完成时,将事先传递的回调函数包装成任务,加入到消息队列的末尾排队,等待主线程调度执行。 在这种异步模式下,浏览器永不阻塞,从而最大限度的保证了单线程的流畅运行。
阐述一下 JS 的事件循环
事件循环又叫做消息循环,是浏览器渲染主线程的工作方式。 在 Chrome 中,它开启一个不会结束的 for 循环,每次循环从消息队列中取出第一个任务执行,而其他线程只需要在合适的时候将任务加入到队列末尾即可。消息队列有多个,在一次事件循环中,由浏览器自行决定取哪一个队列的任务,微队列优先级最高,其次交互队列,然后是延迟队列。当然队列还有很多,这三个是最常见的。
JS 中的计时器能做到精确计时吗
不行,因为
- 计算机硬件没有原子钟,无法做到精确计时
- 操作系统的计时函数本身就有少量偏差,由于 JS 的计时器最终调用的是操作系统的函数,也就是携带了这些偏差。
- 浏览器实现计时器时,如果嵌套层级超过 5 层,则会带有 4 毫秒的最少时间,这样在计时时间少于 4 毫秒时又带来了偏差。
- 受事件循环的影响,计时器的回调函数会放到延迟队列中,延迟队列的优先级比较低,因此又带来了偏差。
EventLoop 相关示例代码输出
<html>
<head>
<script>
(function test() {
setTimeout(() => {
console.log(1);
}, 0);
new Promise((resolve) => {
console.log(2);
for (var i = 0; i < 10000; i++) {
i == 9999 && resolve(3);
}
console.log(4);
}).then((data) => {
console.log(data);
});
console.log(5);
})();
</script>
<script>
(function test2() {
setTimeout(() => {
console.log(6);
}, 0);
new Promise((resolve) => {
console.log(7);
for (var i = 0; i < 10000; i++) {
i == 9999 && resolve(8);
}
console.log(9);
}).then((data) => {
console.log(data);
});
console.log(10);
})();
</script>
</head>
<body></body>
<html></html>
</html>
<!-- 2 4 5 3 7 9 10 8 1 6 -->Promise
Promise 有哪几种状态
- pending: 初始状态, 非 fulfilled 或 rejected.
- fulfilled: 成功的操作.
- rejected: 失败的操作.
then 和 catch 的处理
then 接收两个函数,第一个函数处理 fulfilled,第二个函数处理 rejected,它们正常处理完会 return 一个 fulfilled 状态的 Promise,但如果执行报错或手动throw new Error,那 return 一个 rejected 状态的 Promise;新的 Promise 会被后面的 then 或 catch 对应接收处理。
catch 接收一个函数,处理 rejected,它正常处理完是 return 一个 fulfilled 状态的 Promise,但如果执行报错或手动throw new Error,那 return 一个 rejected 状态的 Promise。
Promise.resolve()
.then(() => {
console.log(1);
})
.catch(() => {
console.log(2);
})
.then(() => {
console.log(3);
});
// 打印1和3Promise.resolve()
.then(() => {
console.log(1);
throw new Error("error");
})
.catch(() => {
console.log(2);
})
.then(() => {
console.log(3);
});
// 打印1和2和3Promise.resolve()
.then(() => {
console.log(1);
throw new Error("error");
})
.catch(() => {
console.log(2);
})
.catch(() => {
console.log(3);
});
// 打印1和2Promise.reject()
.then(() => {
console.log(1);
throw new Error("error");
})
.catch(() => {
console.log(2);
throw new Error("error2");
})
.then(
() => {
console.log(3);
},
() => {
console.log(4);
}
)
.catch(() => {
console.log(5);
});
// 打印2和4async-await 和 Promise 的关系
- 执行 async 函数,返回的是一个 Promise 对象
- await 相当于 Promise 的 then 的第一个函数
- try-catch 相当于 Promise 的 catch
手写 Promise
第一步:
/**
* 1.构造基础的Promise;异步完成通知改状态存结果;捕获意外错误。
*/
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class MyPromise {
_state = PENDING;
_value = undefined;
// Promise的构造函数入参是一个函数
constructor(executor) {
try {
// 该函数立即执行的;异步操作返回后,会用resolve或reject通知Promise
executor(this._resolve.bind(this), this._reject.bind(this));
} catch (error) {
// executor执行可能会报错,例如用户写错语法,那我们自动帮它调用reject
this._reject(error);
}
}
// 异步成功,通知Promise改状态为fulfilled,记录异步结果
_resolve(data) {
this._changeState(FULFILLED, data);
}
// 异步异常,通知Promise改状态为rejected,记录异常原因
_reject(reason) {
this._changeState(REJECTED, reason);
}
// Promise改变状态时,只能由pending变为fulfilled或rejected
_changeState(state, data) {
if (this._state !== PENDING) return;
this._state = state;
this._value = data;
}
}
const p = new MyPromise((resolve, reject) => {
reject(1);
});
console.log(p);
/* new Promise((resolve, reject) => {}) */第二步:
/**
* 1.构造基础的Promise;异步完成通知改状态存结果;捕获意外错误。
*
* 2.then会注册回调函数并返回新Promise;构造队列存储多个回调函数;新Promise的状态由谁决定。
*/
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class MyPromise {
_state = PENDING;
_value = undefined;
/*
同一个Promise对象可以多次调用then,它就会注册很多回调函数,那么就需要将这么多回调函数
放到队列中等待处理
*/
_handlers = [];
constructor(executor) {
try {
executor(this._resolve.bind(this), this._reject.bind(this));
} catch (error) {
this._reject(error);
}
}
/*
同一个Promise对象调用一次then,会注册两个回调函数,第一个回调函数是处理fulfilled的结
果,第二个回调函数处理rejected的结果,then最终会返回一个新的Promise对象
*/
then(onResolved, onRejected) {
/*
新Promise对象的状态是由回调函数执行情况决定的,一般的,回调函数正常执行完那就调用
新Promise对象的resolve,代码执行报错就会调用新Promise对象的reject
*/
return new MyPromise((resolve, reject) => {
this._pushHandler(onResolved, FULFILLED, resolve, reject);
this._pushHandler(onRejected, REJECTED, resolve, reject);
});
}
_resolve(data) {
this._changeState(FULFILLED, data);
}
_reject(reason) {
this._changeState(REJECTED, reason);
}
_changeState(state, data) {
if (this._state !== PENDING) return;
this._state = state;
this._value = data;
}
_pushHandler(executor, state, resolve, reject) {
this._handlers.push({ executor, state, resolve, reject });
}
}
const p = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve(1);
}, 1000);
});
p.then(
function A1() {},
function A2() {}
);
p.then(
function B1() {},
function B2() {}
);
console.log(p);第三步:
/**
* 1.构造基础的Promise;异步完成通知改状态存结果;捕获意外错误。
*
* 2.then会注册回调函数并返回新Promise;构造队列存储多个回调函数;新Promise的状态由谁决定。
*
* 3.回调函数队列执行时机有2个;每次出队列一个执行对象进行处理。
*/
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class MyPromise {
_state = PENDING;
_value = undefined;
_handlers = [];
constructor(executor) {
try {
executor(this._resolve.bind(this), this._reject.bind(this));
} catch (error) {
this._reject(error);
}
}
then(onResolved, onRejected) {
return new MyPromise((resolve, reject) => {
this._pushHandler(onResolved, FULFILLED, resolve, reject);
this._pushHandler(onRejected, REJECTED, resolve, reject);
/*
如果Promise里没有异步直接就resolve或reject了,那么此时then会比改变状态慢一步。
这就导致_changeState里的_runHandlers先执行了,但那时候还没有注册回调函数,那么
就需要在此处再调用一次_runHandlers,确保包含上述场景
*/
this._runHandlers();
});
}
_resolve(data) {
this._changeState(FULFILLED, data);
}
_reject(reason) {
this._changeState(REJECTED, reason);
}
_changeState(state, data) {
if (this._state !== PENDING) return;
this._state = state;
this._value = data;
// 当Promise状态改变后,就会尝试去执行对应的回调函数(先注册回调函数后改变状态)
this._runHandlers();
}
_pushHandler(executor, state, resolve, reject) {
this._handlers.push({ executor, state, resolve, reject });
}
_runHandlers() {
// 要去处理回调了,但此时还是pending状态那就不处理(注册回调时,可能还在异步还是pending)
if (this._state === PENDING) return;
console.log(`处理了${this._handlers.length}个函数`);
console.log("this._handlers", this._handlers);
// 每次从开头取一个出来处理,处理完后删除
while (this._handlers[0]) {
const handler = this._handlers[0];
this._runOneHandler(handler);
this._handlers.shift();
}
}
_runOneHandler() {}
}
const p = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve(1);
});
});
p.then(function A1() {});
setTimeout(() => {
p.then(function A2() {});
});第四步:
// 放入微队列
function myQueueMicroTask(task) {
if (process && process.nextTick) {
process.nextTick(task);
} else if (MutationObserver) {
const p = document.createElement("p");
const observer = new MutationObserver(task);
observer.observe(p, { childList: true });
p.innerHTML = "test";
} else if (queueMicrotask) {
queueMicrotask(task);
} else {
setTimeout(task);
}
}/**
* 1.构造基础的Promise;异步完成通知改状态存结果;捕获意外错误。
*
* 2.then会注册回调函数并返回新Promise;构造队列存储多个回调函数;新Promise的状态由谁决定。
*
* 3.回调函数队列执行时机有2个;每次出队列一个执行对象进行处理。
*
* 4.根据回调函数类型决定是直接执行还是状态穿透,函数返回本身是Promise那就用它的then来处理。
*/
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class MyPromise {
_state = PENDING;
_value = undefined;
_handlers = [];
constructor(executor) {
try {
executor(this._resolve.bind(this), this._reject.bind(this));
} catch (error) {
this._reject(error);
}
}
then(onResolved, onRejected) {
return new MyPromise((resolve, reject) => {
this._pushHandler(onResolved, FULFILLED, resolve, reject);
this._pushHandler(onRejected, REJECTED, resolve, reject);
this._runHandlers();
});
}
_resolve(data) {
this._changeState(FULFILLED, data);
}
_reject(reason) {
this._changeState(REJECTED, reason);
}
_changeState(state, data) {
if (this._state !== PENDING) return;
this._state = state;
this._value = data;
this._runHandlers();
}
_pushHandler(executor, state, resolve, reject) {
this._handlers.push({ executor, state, resolve, reject });
}
_runHandlers() {
if (this._state === PENDING) return;
while (this._handlers[0]) {
const handler = this._handlers[0];
this._runOneHandler(handler);
this._handlers.shift();
}
}
_runOneHandler({ executor, state, resolve, reject }) {
queueMicrotask(() => {
// 状态匹配上才执行
if (state !== this._state) return;
// 如果不是函数,那么状态穿透
if (typeof executor !== "function") {
// 意思是then的Promise状态和结果,会作为then返回新Promise的状态和结果
this._state === FULFILLED ? resolve(this._value) : reject(this._value);
return;
}
try {
// 是函数,还要分执行结果是普通值还是Promise
const rlt = executor(this._value);
// 是Promise,那么这个Promise的状态和结果会决定then返回新Promise的状态和结果
if (isPromise(rlt)) {
rlt.then(resolve, reject);
} else {
resolve(rlt);
}
} catch (error) {
// 函数执行报错那就直接reject
reject(error);
}
});
}
}
// 是对象并且具有属性then,这个then属性是一个函数,那么这个对象就是Promise对象
function isPromise(param) {
return !!(param && typeof param === "object" && typeof param.then === "function");
}
const p1 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve(1);
});
});
const p2 = p1.then((data) => {
console.log(data);
return new MyPromise((resolve, reject) => {
resolve("a");
});
});
setTimeout(() => {
console.log(p2);
}, 50);完整的 PromiseA+
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
// 放入微队列
const myQueueMicroTask = (task) => {
if (process && process.nextTick) {
// node
process.nextTick(task);
} else if (MutationObserver) {
// 浏览器
const p = document.createElement("p");
const observer = new MutationObserver(task);
observer.observe(p, { childList: true });
p.innerHTML = "test";
} else if (queueMicrotask) {
// 新API
queueMicrotask(task);
} else {
// 最差的情况
setTimeout(task);
}
};
/**
* 判断入参是否符合Promise形式,规则:param是普通对象或者函数,不能是基础类型,它有一个
* then属性,该属性得是一个函数
* @param {*} param
* @returns then或者null
* 这个thenable有一种特殊形式,param.then抛错,此时调用thenable()的地方就应该try-catch处理
* Object.create(null, {
then: {
get: function () {
throw reason;
}
}
});
*/
const thenable = (param) => {
if ((typeof param === "object" && param != null) || typeof param === "function") {
// 单独取then,因为要过promises-aplus-tests的2.3.3.1检测,即只执行一次“param.then”
const then = param.then;
if (typeof then === "function") {
return then;
}
}
return null;
};
class Mypromise {
_state = PENDING;
_value = undefined;
_reason = undefined;
/*
同一个Promise对象可以多次调用then,它就会注册很多回调函数,那么就需要将这么多回调函数
放到队列中等待处理
*/
_handlers = [];
// Promise的构造函数入参是一个函数
constructor(executor) {
try {
// executor立即执行的;executor内部的异步操作完成后,会用resolve或reject通知Promise
executor(this._resolve, this._reject);
} catch (error) {
// executor执行可能会报错,例如用户写错语法,那我们自动帮它调用reject
this._reject(error);
}
}
/*
同一个Promise对象调用一次then,会注册两个回调函数,第一个回调函数是处理fulfilled的结
果,第二个回调函数处理rejected的结果,then最终会返回一个新的Promise对象
*/
then(onResolved, onRejected) {
/*
新Promise对象的状态是由回调函数执行情况决定的,一般的,回调函数正常执行完那就调用
新Promise对象的resolve,代码执行报错就会调用新Promise对象的reject
*/
let res, rej;
const promise = new Mypromise((resolve, reject) => {
// 不能将_pushHandler放这里,原因是新promise没有完全生成,所以得放到new Mypromise后
res = resolve;
rej = reject;
});
// onResolved和onRejected返回的对象不能是这个新promise,所以要将promise传进去
this._pushHandler(FULFILLED, onResolved, res, rej, promise);
this._pushHandler(REJECTED, onRejected, res, rej, promise);
/*
一般情况下,then会比异步先执行,也就是回调函数先注册好了,等待异步执行完就可以
调用回调函数了,这完全没有问题。(先注册回调函数,后执行回调函数)
但是有一种情况,就是executor内没有异步操作,直接resolve或reject了,然后执行链式
调用then,这个时候才注册好回调函数。(先执行回调函数,后才注册回调函数,行不通)
解决:在链式调用then注册好回调函数,就尝试执行一次回调函数,如果state是pending
状态时就自动跳过执行。(注册回调函数,遇到pending跳过执行,等待状态改变再执行)
*/
this._runHandlers(); // push后,也就是注册好回调函数,此时就尝试执行回调函数
return promise;
}
// 异步成功,通知Promise改状态为fulfilled,记录异步结果
_resolve = (value) => {
this._changeState(FULFILLED, value);
};
// 异步异常,通知Promise改状态为rejected,记录异常原因
_reject = (reason) => {
this._changeState(REJECTED, reason);
};
_changeState(state, data) {
// Promise改变状态时,只能由pending变为fulfilled或rejected
if (this._state !== PENDING) return;
this._state = state;
state === FULFILLED ? (this._value = data) : (this._reason = data);
// 当Promise状态改变后,就会尝试去执行对应的回调函数(先注册回调函数,后执行回调函数)
this._runHandlers();
}
_pushHandler(state, executor, resolve, reject, promise) {
this._handlers.push({ state, executor, resolve, reject, promise });
}
_runHandlers() {
// 要去处理回调了,但此时还是pending状态那就不处理(注册回调时,可能还在异步还是pending)
if (this._state === PENDING) return;
// 每次从开头取一个出来处理,处理完后删除
while (this._handlers[0]) {
const handler = this._handlers[0];
this._runOneHandler(handler);
this._handlers.shift();
}
}
_runOneHandler({ state, executor, resolve, reject, promise }) {
// 放入微队列执行
myQueueMicroTask(() => {
// 状态匹配上才执行,此时状态只会是fulfilled或者rejected,只执行对应的回调
if (state !== this._state) return;
// 如果不是函数,那么状态穿透,也就是将本Promise对象的状态直接交给新Promise对象
if (typeof executor !== "function") {
// 意思是then前的Promise状态和结果,会作为then后新的Promise的状态和结果
this._state === FULFILLED ? resolve(this._value) : reject(this._reason);
return;
}
try {
// 是函数,还要分执行结果是普通值还是Promise
const rlt = executor(this._state === FULFILLED ? this._value : this._reason);
this._handleExecutRlt(rlt, resolve, reject, promise);
} catch (error) {
// 函数执行报错那就直接reject
reject(error);
}
});
}
_handleExecutRlt(rlt, resolve, reject, promise) {
// executor执行返回的结果不能是then返回的结果 Promise/A+ 2.3.1
if (rlt === promise) return reject(new TypeError("Promise循环引用了,Promise/A+ 2.3.1"));
let called;
try {
const then = thenable(rlt);
/*
判断rlt是否是一个Promise形式,是的话就执行它的then,用then的执行情况决定是resolve
还是reject。then执行后又是一个Promise形式,就继续解析,直到它是一个普通值。
*/
if (then) {
then.call(
rlt,
(val) => {
// Promise/A+ 2.3.3.3.3 只能调用一次
if (called) return;
called = true;
// 如果val还是一个Promise形式的,那么就递归处理它,直到是普通值
this._handleExecutRlt(val, resolve, reject, promise);
},
(r) => {
// Promise/A+ 2.3.3.3.3 只能调用一次
if (called) return;
called = true;
reject(r);
}
);
} else {
// 是普通值,将这个执行结果包成新Promise供后面的人进行链式调用
resolve(rlt);
}
} catch (error) {
// Promise/A+ 2.3.3.3.3 只能调用一次
if (called) return;
called = true;
reject(error);
}
}
}
module.exports = Mypromise;手写 Promise 的 catch 和 finally
class MyPromise {
catch(onRejected) {
let res, rej;
const promise = new MyPromise((resolve, reject) => {
(res = resolve), (rej = reject);
});
this._pushHandler(REJECTED, onRejected, res, rej, promise);
this._runHandlers();
return promise;
}
}class MyPromise {
finally(onFinished) {
return this.then(
(value) => {
onFinished(); // 执行回调,接收和返回的数据都不做处理
return value;
},
(reason) => {
onFinished(); // 执行回调,接收和返回的数据都不做处理
throw reason;
}
);
}
}手写 Promise.all 和 race
class MyPromise {
// 等到所有的 promise 对象都成功或有任意一个 promise 失败。
static all(params) {
return new MyPromise((resolve, reject) => {
try {
let count = 0;
let fulfilledCount = 0;
const rlt = [];
for (const p of params) {
const i = count;
count++;
MyPromise.resolve(p).then((val) => {
rlt[i];
if (++fulfilledCount === count) {
resolve(rlt);
}
}, reject);
}
if (!count) resolve([]);
} catch (error) {
reject(error);
}
});
}
}class MyPromise {
// 等到任意一个 promise 的状态变为已敲定。
static race(params) {
return new MyPromise((resolve, reject) => {
try {
for (const p of params) {
MyPromise.resolve(p).then(resolve, reject);
}
// 如果是[],就永远pending
} catch (error) {
reject(error);
}
});
}
}手写 Promise.allSettled 和 any
class MyPromise {
// 等到所有 promise 都已敲定(每个 promise 都已兑现或已拒绝)
static allSettled(params) {
return new MyPromise((resolve, reject) => {
try {
let count = 0;
let settledCount = 0;
const rlt = [];
for (const p of params) {
const i = count;
count++;
MyPromise.resolve(p).then(
(value) => {
rlt[i] = { state: FULFILLED, value };
if (++settledCount === count) {
resolve(rlt);
}
},
(reason) => {
rlt[i] = { state: REJECTED, reason };
if (++settledCount === count) {
resolve(rlt);
}
}
);
}
if (!count) resolve([]);
} catch (error) {
reject(error);
}
});
}
}class MyPromise {
// 任意一个 promise 变成了兑现状态那就返回已兑现的promise,
// 最终都没有兑现,那就返回拒绝的promise
static any(params) {
return new MyPromise((resolve, reject) => {
try {
let count = 0;
let handleCount = 0;
const errors = [];
for (const p of params) {
const i = count;
count++;
MyPromise.resolve(p).then(resolve, () => {
errors[i] = new Error("xxx");
if (++handleCount === count) {
reject(
new AggregateError({
name: "AggregateError",
message: "All Promises rejected",
errors,
})
);
}
});
}
if (!count) {
reject(
new AggregateError({
name: "AggregateError",
message: "params is empty",
errors: [],
})
);
}
} catch (error) {
reject(error);
}
});
}
}