# 异步
# 回调
异步执行某项功能时,我们不知道这异步什么时候结束,于是可以使用回调函数,使异步执行结束时加载我们想要执行的代码。
但是,当我们想要在回调结束在执行下一个回调,再下一个回调...,于是出现了厄运金字塔(回调地狱)。
# Promise
Promise 对象的构造器(constructor)语法如下:
let promise = new Promise(function(resolve, reject) {
// executor(生产者代码),promise中的代码会被立即调用
});
2
3
resolve, reject 为其两个参数,当 executor 获得了结果,无论是早还是晚,它会调用这两个回调之一:
resolve(value)
—— 如果任务成功完成并带有结果value
。reject(error)
—— 如果出现了 error,error
即为 error 对象。这两个函数已经由 JavaScipt 引擎预先定义,因此我们不需要创建它们。
由 new Promise 构造器返回的 promise 对象具有以下内部属性:
state
—— 最初是"pending"
,然后在 resolve 被调用时变为"fulfilled"
,或者在 reject 被调用时变为"rejected"
。result
—— 最初是undefined
,然后在 resolve(value) 被调用时变为value
,或者在 reject(error) 被调用时变为error
。
# .then
promise.then(
function(result) { /* 状态为成功时 */ },
function(error) { /* 状态为错误时 */ }
);
2
3
4
# .catch
只捕捉错误时的状态,在报错时执行。
.catch(f)
调用是 .then(null, f)
的完全的模拟,它只是一个简写形式。
一般总是建议,Promise 对象后面要跟catch()方法,这样可以处理 Promise 内部发生的错误。
# .finally
无论 promise 的处理结果如何最终都会执行,且会将 promise 的执行结果传递给下一个处理程序,也就是说后面还可以跟 .then 和 .catch 。
# 隐式 try...catch
new Promise((resolve, reject) => {
throw new Error("Whoops!");
}).catch(alert); // Error: Whoops!
2
3
类似于
try{
//...代码逻辑
}.catch(f)
2
3
其内代码出现异常,会被默认视作执行 reject
# Promise API
# Promise.all
Promise.all([
new Promise(resolve => setTimeout(() => resolve(1), 3000)), // 1
new Promise(resolve => setTimeout(() => resolve(2), 2000)), // 2
new Promise(resolve => setTimeout(() => resolve(3), 1000)) // 3
]).then(alert); // 1,2,3 当上面这些 promise 准备好时:每个 promise 都贡献了数组中的一个元素
2
3
4
5
当所有给定的 promise 都 resolve 时,新的 promise 才会 resolve,并且其结果数组将成为新 promise 的结果
且结果数组中元素的顺序与其在源 promise 中的顺序相同。即使第一个 promise 花费了最长的时间才 resolve,但它仍是结果数组中的第一个。
但只要有一个 Promise 出错那么 Promise.all() 就只会输出这一个错误
# Promise.allSettled
Promise.allSettled 与Promise类似,但其会等待所有的 promise 都被 settle,无论结果如何,
即使有 Promise 输出错误,也会获取到其他 Promise 的输出。
# Promise.race
与 Promise.all 类似,但只等待第一个 settled 的 promise 并获取其结果(或 error)。
# Promise.any
与 Promise.race 类似,区别在于 Promise.any 只等待第一个 fulfilled (resolve被调用)的 promise,并将这个 fulfilled 的 promise 返回。
# Async/await
# async
async function f() {
return 1;
}.then(alert); // 1
2
3
在函数前面的 “async” 这个单词表达了一个简单的事情:即这个函数总是返回一个 promise。
# Await
await 只在 async 函数内工作
async function f() {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("done!"), 1000)
});
let result = await promise; // 等待,直到 promise resolve (*)
alert(result); // "done!"
}
f();//1秒后显示"done!"
2
3
4
5
6
7
8
关键字 await 让 JavaScript 引擎等待直到 promise 完成(settle)并返回结果。
# event loop事件循环
js是单线程的, 为了防止单线程的任务阻塞,就有了异步任务。
过程:
遇到同步任务,同步任务被依次压入执行栈中;
遇到异步任务,异步处理机制等异步回调结束会将异步任务加入任务队列;
当执行栈中的所有同步任务被处理完毕,异步处理机制再将任务队列中的异步任务依次加入执行栈。
而事件循环
就是反复查看执行栈和任务队列的过程
# 宏任务和微任务
宏任务:setTimeout,setInterval,Ajax,DOM 事件。 微任务:Promise async/await。
两者区别:
- 宏任务:DOM 渲染后触发,如
setTimeout
、setInterval
、DOM 事件
、script
。 - 微任务:DOM 渲染前触发,如
Promise.then
、MutationObserver
、Node 环境下的process.nextTick
。
而渲染是在执行栈空了之后开始,优先级比任务队列高,即渲染在下一个异步任务加入栈前执行。
注意 Promise(其构造函数)内的代码会立即执行,同同步任务表现一致。