异步JS
查看更多学习笔记:GitHub:LoveEmiliaForever
MDN中文官网
异步编程简介
异步编程
赋予程序如下特性
- 通过调用一个函数来启动一个长期运行的操作
- 让函数开始操作并立即返回,这样我们的程序就可以保持对其他事件做出反应的能力
- 当操作最终完成时,通知我们操作的结果
浏览器中许多高耗时功能都是异步实现的
fetch()
请求资源getUserMedid()
访问用户的多媒体设备showOpenFilePicker()
用户选择图片
事件处理程序实际上就是异步编程的一种形式:你提供的函数(事件处理程序)将在事件发生时被调用(而不是立即被调用)
回调函数
是一个被传递到另一个函数中的会在适当的时候被调用的函数
回调函数曾经是 JavaScript 中实现异步函数的主要方式
但是,一但回调函数嵌套起来,那就让代码变的难以理解,称为回调地狱
或厄运金字塔
// 回调地狱示例
function doStep1(init, callback) {const result = init + 1;callback(result);
}
function doStep2(init, callback) {const result = init + 2;callback(result);
}
function doStep3(init, callback) {const result = init + 3;callback(result);
}
function doOperation() {doStep1(0, (result1) => {doStep2(result1, (result2) => {doStep3(result2, (result3) => {console.log(`结果:${result3}`);});});});
}
doOperation();
面对这样的嵌套回调,处理错误也会变得非常困难:你必须在“金字塔”的每一级处理错误,而不是在最高一级一次完成错误处理
Promise的使用
在基于 Promise 的 API 中,异步函数会启动操作并返回 Promise 对象
然后,你可以将处理函数附加到 Promise 对象上
当操作完成时(成功或失败),这些处理函数将被执行
fetch()
fetch()
API,一个现代的、基于 Promise
的、用于替代 XMLHttpRequest
的方法
// 异步操作,请求资源
const fetchPromise = fetch("https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json",
);console.log(fetchPromise);// 绑定处理函数到 成功 状态上
fetchPromise.then((response) => {console.log(`已收到响应:${response.status}`);
});console.log("已发送请求……");// Promise { <state>: "pending" }
// 已发送请求……
// 已收到响应:200
链式调用Promise
可以一步一步的定义异步操作的顺序,且是通过链式调用的方式,而不是嵌套
const fetchPromise = fetch("https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json",
);fetchPromise.then((response) => {return response.json();}).then((json) => {console.log(json[0].name);});
错误捕获
Promise
对象提供了一个 catch()
方法来支持错误处理
当异步操作失败时,传递给 catch()
的处理函数被调用
如果将 catch()
添加到 Promise
链的末尾,它就可以在任何异步函数失败时被调用
const fetchPromise = fetch("bad-scheme://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json",
);fetchPromise.then((response) => {if (!response.ok) {throw new Error(`HTTP 请求错误:${response.status}`);}return response.json();}).then((json) => {console.log(json[0].name);}).catch((error) => {console.error(`无法获取产品列表:${error}`);});
Promise状态
- 待定
pending
:初始状态,既没有被兑现,也没有被拒绝,还在运行中 - 已兑现
fulfilled
:意味着操作成功,当 Promise 完成时,它的 then() 处理函数被调用 - 已拒绝
rejected
:意味着操作失败,当 Promise 失败时,它的 catch() 处理函数被调用 - 已敲定
settled
:同时表示fulfilled
和rejected
两种情况是否出现了其一 - 已决议
resolved
:被认为是fulfilled
Promise的逻辑运算(非且或)
某些情况下,需要规定多个Promise之间的运行逻辑
Promise.all()
,接收一个 Promise 数组,并返回一个单一的 Promise- 数组中所有的 Promise 都被兑现时,才通知 then() 处理函数,并提供响应数组,响应数组顺序与 Promise 数组顺序相同
- Promise数组中任意 Promise 被拒绝,catch() 处理函数被调用,并提供被拒绝的 Promise 所抛出的错误
Promise.any()
- 任意一个Promise兑现,则启动then()
- 全部Promise拒绝,则启动catch()
async和await
在一个函数的开头添加 async
,就可以使其成为一个异步函数
异步函数总是返回一个 Pomise
,因此处理异步函数应该使用then()
或catch()
在调用一个返回 Promise
的函数之前使用 await
关键字
这使得代码在该点上等待,直到 Promise
被完成,这时 Promise
的响应被当作返回值,或者被拒绝的响应被作为错误抛出
workers多线程
Workers
给了你在不同线程中运行某些任务的能力,因此你可以多线程操作
对于多线程代码,你永远不知道你的线程什么时候将会被挂起,以让步于其它线程
你的主代码和你的 worker 代码永远不能直接访问彼此的变量,只有通过相互发送消息来进行交互
这意味着 workers 不能访问 DOM(窗口、文档、页面元素等等)
workers共有三种类型
dedicated workers
:它由一个脚本实例使用shared workers
:由运行在不同窗口中的多个不同脚本共享service workers
:像代理服务器,缓存资源以便于 web 应用程序可以在用户离线时工作
运行workers
- 主代码和workers代码存放于不同
js文件
- 主代码
- 使用
const worker = new Worker("./workers.js")
在主代码中创建worker - 使用
worker.postMessage()
向 worker 发送消息 - 向 worker 添加一个
message 消息处理器(监听message事件的监听器)
,以处理worker传过来的消息
- 使用
- worker代码
- 添加一个
监听message事件的监听器
,以对主代码传过来的消息做出反应 - 在 message 事件处理器内部,事件的
data 属性
包含一个来自主脚本的参数的副本 - 用
postMessage()
向主代码传递消息
- 添加一个