异步
JS 中的几种异步场景。
setTimeout,setInterval- 网络请求
- 事件绑定
主要是因为 JS 是单线程的。
所以为了实现异步, JS 实现了事件循环机制。
循环过程如下:
-
执行
script主线程, -
遇到
setTimeout,setInterval等宏任务,丢到宏任务队列中去,遇到Promise,nextTick等微任务,丢到微任务队列中去。 -
当前宏任务执行完毕之后,拉取所有微任务队列中的数据,全部执行完毕。
-
再取宏任务队列中的第一个任务执行。回到 2。
事件绑定
一个简单的通用的事件绑定函数:
function bindEvent(elem, type, selector, fn) {
if (fn == null) {
fn = selector;
selector = null;
}
elem.addEventListener(type, function (e) {
var target;
if (selector) {
target = e.target;
if (target.matches(selector)) {
fn.call(target, e);
}
} else {
fn(e);
}
});
}
网络请求
一个简单的基于原生 XMLHTTPRequest 实现的网络请求
var xhr = new XMLHttpRequest();
xhr.open("GET", "/api", false);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status === 200) {
alert(xhr.responseText);
}
}
};
xhr.send(null);
readyState 的几种状态:
- 0 - 未初始化,还没有调用 send()方法
- 1 - 载入,已调用
send()方法,正在发送请求 - 2 - 载入完成,
send()方法执行完成,已经接收到全部响应内容 - 3 - 交互,正在解析响应内容
- 4 - 完成,响应内容解析完成,可以在客户端调用了
Promise
// 步骤一:了解promise规范
// 步骤二:实现
// 步骤三:测试
const statusMap = {
PENDING: "pending",
FULFILLED: "fulfilled",
REJECTED: "rejected",
};
// 将promise设置为fulfilled状态
function fulfilledPromise(promise, value) {
// 只能从pending状态转换为其他状态
if (promise.status !== statusMap.PENDING) {
return;
}
promise.status = statusMap.FULFILLED;
promise.value = value;
runCbs(promise.fulfilledCbs, value);
}
// 将promise设置为rejected状态
function rejectedPromise(promise, reason) {
// 只能从pending状态转换为其他状态
if (promise.status !== statusMap.PENDING) {
return;
}
promise.status = statusMap.REJECTED;
promise.reason = reason;
runCbs(promise.rejectedCbs, reason);
}
function runCbs(cbs, value) {
cbs.forEach((cb) => cb(value));
}
function isFunction(fn) {
return (
Object.prototype.toString.call(fn).toLocaleLowerCase() ===
"[object function]"
);
}
function isObject(obj) {
return (
Object.prototype.toString.call(obj).toLocaleLowerCase() ===
"[object object]"
);
}
function isPromise(p) {
return p instanceof Promise;
}
// promise的解析
function resolvePromise(promise, x) {
// x 与promise相同
if (promise === x) {
rejectedPromise(promise, new TypeError("cant be the same"));
return;
}
// x 是promise
if (isPromise(x)) {
if (x.status === statusMap.FULFILLED) {
fulfilledPromise(promise, x.value);
return;
}
if (x.status === statusMap.REJECTED) {
rejectedPromise(promise, x.reason);
return;
}
if (x.status === statusMap.PENDING) {
x.then(
() => {
fulfilledPromise(promise, x.value);
},
() => {
rejectedPromise(promise, x.reason);
}
);
return;
}
return;
}
if (isObject(x) || isFunction(x)) {
let then;
let called = false;
try {
then = x.then;
} catch (error) {
rejectedPromise(promise, error);
return;
}
if (isFunction(then)) {
try {
then.call(
x,
(y) => {
if (called) {
return;
}
called = true;
resolvePromise(promise, y);
},
(r) => {
if (called) {
return;
}
called = true;
rejectedPromise(promise, r);
}
);
} catch (error) {
if (called) {
return;
}
called = true;
rejectedPromise(promise, error);
}
return;
} else {
fulfilledPromise(promise, x);
return;
} // x不是对象或者函数
} else {
fulfilledPromise(promise, x);
return;
}
}
class Promise {
constructor(fn) {
this.status = statusMap.PENDING;
this.value = undefined;
this.reason = undefined;
this.fulfilledCbs = []; // then fulfilled callback
this.rejectedCbs = []; // then rejected callback
fn(
(value) => {
// 防止直接传进来一个thenalbe
// fulfilledPromise(this, value);
resolvePromise(this, value);
},
(reason) => {
rejectedPromise(this, reason);
}
);
}
// 两个参数
then(onFulfilled, onRejected) {
const promise1 = this;
const promise2 = new Promise(() => {});
if (promise1.status === statusMap.FULFILLED) {
if (!isFunction(onFulfilled)) {
return promise1;
}
setTimeout(() => {
try {
const x = onFulfilled(promise1.value);
resolvePromise(promise2, x);
} catch (error) {
rejectedPromise(promise2, error);
}
}, 0);
}
if (promise1.status === statusMap.REJECTED) {
if (!isFunction(onRejected)) {
return promise1;
}
setTimeout(() => {
try {
const x = onRejected(promise1.reason);
resolvePromise(promise2, x);
} catch (error) {
rejectedPromise(promise2, error);
}
}, 0);
}
if (promise1.status === statusMap.PENDING) {
onFulfilled = isFunction(onFulfilled)
? onFulfilled
: (value) => {
return value;
};
onRejected = isFunction(onRejected)
? onRejected
: (err) => {
throw err;
};
promise1.fulfilledCbs.push(() => {
setTimeout(() => {
try {
const x = onFulfilled(promise1.value);
resolvePromise(promise2, x);
} catch (error) {
rejectedPromise(promise2, error);
}
}, 0);
});
promise1.rejectedCbs.push(() => {
setTimeout(() => {
try {
const x = onRejected(promise1.reason);
resolvePromise(promise2, x);
} catch (error) {
rejectedPromise(promise2, error);
}
}, 0);
});
}
return promise2;
}
}
// 测试用到的钩子
Promise.deferred = function () {
const deferred = {};
deferred.promise = new Promise((resolve, reject) => {
deferred.resolve = resolve;
deferred.reject = reject;
});
return deferred;
};
module.exports = Promise;
Generator
一个可迭代的对象的例子:
function createIterator(items) {
var i = 0;
return {
next: function () {
var done = i >= items.length;
var value = !done ? items[i++] : undefined;
return {
done: done,
value: value,
};
},
};
}
var iterator = createIterator([1, 2, 3]);
iterator.next();
iterator.next();
iterator.next();
iterator.next();
使用 yield 的情况, yield 会替换前一个的返回值。
// yield 例子
function* createIterator() {
let first = yield 1;
let second = yield first + 2;
yield second + 3;
}
let iterator = createIterator();
iterator.next();
iterator.next(4);
iterator.next(5);
iterator.next();
Thunk 函数自动执行 generator:
// 代码
function run(fn) {
var gen = fn(); //获得生成器对象
// 递归调用自身的方法(类似尾递归的思路,执行完之后调用自身)
function next(err, data) {
// 获取生成器next执行之后的返回值。
var result = gen.next(data);
// 如果done了,就返回
if (result.done) return;
// 如果没有done,就调用自身,给fs.readFile的回调,让它在执行完之后
// 执行next,next的传参是callback的传参
result.value(next);
}
// 先执行一次
next();
}
const Thunk = function (fn) {
return function (...args) {
return function (callback) {
return fn.call(this, ...args, callback);
};
};
};
// 使用
const fs = require("fs");
const readFileThunk = Thunk(fs.readFile);
const g = function* () {
const s1 = yield readFileThunk("xxx.xx");
console.log(s1);
const s2 = yield readFileThunk("xxx.xx");
console.log(s2);
};
async/await
async/await 其实就是 generator 的语法糖, await 会被转译成 yield ,然后通过 generator 自执行来完成异步。
其实就是使用了 Promise.then 来完成上面的 callback 完成的东西,保证了返回值是 Promise。
async function example(params) {
// xxx
}
// =>
function example(params) {
return spawn(function* () {
// xxx
});
}
function spawn(genF) {
return new Promise(function (resolve, reject) {
const gen = genF(); // 生成器对象
function step(nextF) {
let next;
try {
next = nextF(); // 执行gen.next
} catch (e) {
return reject(e);
}
if (next.done) {
return resolve(next.value);
}
Promise.resolve(next.value).then(
function (v) {
step(function () {
return gen.next(v);
});
},
function (e) {
step(function () {
return gen.throw(e);
});
}
);
}
step(function () {
return gen.next(undefined);
});
});
}