说说 JS 异步发展史
查看详情
- 回调函数
- 回调地狱
- 不能使用 try catch 捕获错误
- 嵌套函数存在耦合性,一旦有所改动,就会牵一发而动全身
- 最早解决了同步的问题
- Promise 实现了链式调用
- 解决了回调地狱的问题
- 无法取消 Promise 状态
- 错误需要通过回调函数来捕获
- Generator
- 可以控制函数的执行
- async/await
- 代码清晰,处理了回调地狱的问题
- 如果多个异步操作没有依赖性使用 await 会导致性能上的降低,建议一同触发
常见的异步任务
查看详情
- setTimeout/setInterval
- ajax
- 回调函数
- async await
- promise
promise 有几种状态,promise 有什么优缺点?
查看详情
- 三种状态:
- pending
- fulfilled
- rejected
- 优点:
- 一旦状态改变就不能在变,任何时候都可以得到这个结果
- 可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数
- 缺点:
- 无法取消 promise
- 当处于 pending 状态时,无法得知目前进行到哪一阶段
promise 构造函数是同步执行的还是异步执行的?then 中的方法呢?
查看详情
- promise 的构造函数是同步执行的
- then 中的方法是异步执行的
谈谈事件循环(EventLoop)
查看详情
- JavaScript 是一门单线程的脚本语言,他的异步是通过 EventLoop 来实现的,分为三个部分:调用栈,微任务队列和宏任务队列
- 事件循环的工作流程如下:
- 执行全局代码:首先,JavaScript 解析并执行全局代码,将所有同步任务放入调用栈。
- 调用栈为空时:一旦调用栈为空(即所有同步任务执行完毕),事件循环会检查微任务队列是否为空。
- 执行微任务:如果微任务队列不为空,事件循环会依次执行所有微任务,直到微任务队列为空。
- 执行宏任务:当微任务队列为空后,事件循环会从宏任务队列中取出第一个任务并执行其回调函数。
- 重复上述步骤:事件循环不断重复上述步骤,直到程序终止。
常见的宏任务和微任务
查看详情
- 宏任务
- setTimeout
- setInterval
- requestAnimationFrame
- 微任务
- promise.then
- promise.catch
- promise.finally
- await 后的代码(async 方法中 await 后跟的方法以及它们前的代码都是同步执行的,但由于 await 隐式返回 promise,它后边的代码类似于 promise.then 中的代码,属于微任务)
javascript
async function async1() {
console.log(1);
const result = await async2();
console.log(3);
}
async function async2() {
console.log(2);
}
Promise.resolve().then(() => {
console.log(4);
});
setTimeout(() => {
console.log(5);
});
async1();
console.log(6);
// 答案:1 2 6 4 3 5
如何实现 Promise.all
查看详情
- Promise.all 的功能:将多个 Promise 实例包装成一个新的 Promise 实例, p = Promise.all([p1, p2, p3])
- 只有 p1, p2, p3 状态都变为 fulfilled,p 的状态才会变为 fulfilled,此时 p1, p2, p3 的返回值组成一个数组传递给回调函数
- 只要 p1, p2, p3 之中一个被 rejected,p 的状态就变为 rejected,此时第一个被 reject 的实例的返回值会传给 p 的回调函数
javascript
Promise.all = function (promises) {
return new Promise((resolve, reject) => {
let index = 0;
let result = [];
if (promises.length === 0) {
resolve(result);
} else {
function processValue(i, data) {
result[i] = data;
if (++index === promises.length) {
resolve(result);
}
}
for (let i = 0; i < promises.length; i++) {
//promises[i] 可能是普通值
Promise.resolve(promises[i]).then(
(data) => {
processValue(i, data);
},
(err) => {
reject(err);
return;
}
);
}
}
});
};
如何实现 Promise.race?
查看详情
- 同样是将多个 Promise 实例包装成一个新的 Promise 实例。不同的是只要有一个先改变状态,新 Promise 的状态就会跟着改变。
- 如果传入的参数是不可迭代的将抛出错误
- 如果传入的参数数组是空,那么返回的 promise 将永远等待
javascript
Promise.race = function (promises) {
//promises 必须是一个可遍历的数据结构,否则抛错
return new Promise((resolve, reject) => {
if (typeof promises[Symbol.iterator] !== 'function') {
//真实不是这个错误
Promise.reject('args is not iteratable!');
}
if (promises.length === 0) {
return;
} else {
for (let i = 0; i < promises.length; i++) {
Promise.resolve(promises[i]).then(
(data) => {
resolve(data);
return;
},
(err) => {
reject(err);
return;
}
);
}
}
});
};
如何实现 Promise.finally
查看详情
- 不管 Promise 对象最后的状态如何都会执行。
javascript
Promise.prototype.finally = function (callback) {
return this.then(
(value) => {
return Promise.resolve(callback()).then(() => {
return value;
});
},
(err) => {
return Promise.resolve(callback()).then(() => {
throw err;
});
}
);
};
谈谈对 async/await 的理解
查看详情
- async/await 是基于 Promise 实现的,与 Promise 一样是非阻塞的
- async/await 使得异步代码看起来像同步代码一样
- async 函数返回一个 Promise 对象,可以用 then 指定下一步操作
- async 函数内部抛出的错误会导致返回的 Promise 对象变为 reject 状态,错误对象会被 catch 方法接收到
- async 函数返回的 Promise 对象必须等到内部所有的 await 命令后面的 Promise 对象执行完才会发生状态改变,除非遇见 return 语句或者抛出错误
- async 函数就是 Generator 函数的语法糖,它内置执行器,与普通函数一样;比起星号和 yield 来说,async/await 更加语义化
- async 函数的 await 命令后面可以是 Promise 对象和原始类型的值,适用性更广;如果不是 Promise 对象,会被转成一个立即 resolve 的 Promise 对象。
async 实现的原理是什么?
查看详情
- async 函数的实现原理就是将 Generator 函数和自动执行器,包装在一个函数里。
javascript
async function fn(args) {
const res = await XX;
}
// 等同于(spawn函数就是自动执行器)
function fn(args) {
return spawn(function* () {
const res = yield XX;
});
}
使用 async/await 需要注意什么?
查看详情
- 最好把 await 命令放在 try...catch 代码块中
- 多个 await 命令后面的异步操作,如果不存在继发关系,最好让它们同时触发。
// 写法一
let [foo, bar] = await Promise.all([getFoo(), getBar()]);
// 写法二
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;
- await 命令只能用在 async 函数之中,如果用在普通函数,就会报错