1.xhr与fetch异步编程的步骤,实例演示
1.1 同步与异步/进程与线程
(一)同步与异步
- 同步: 顺序执行, 优点: 静态预判结果可控, 缺点: 耗时任务阻塞执行
- 异步: 乱序执行, 优点: 不会阻塞代码,体验好, 缺点: 顺序不可控
以银行排队办业务为例
- 同步: 默认排队叫号, 依次办理
- 异步: 耗时任务(如修改密码忘带shen份证)则离开队列, 后面任务继续
- 任务队列: 取了shen份证回来了, 就待在”任务队列”中等待再次叫号
哪些是异步任务(耗时)?
- 定时任务:
setTimeout, setInterval
- 事件监听:
addEventListener
- 网络请求:
ajax, promise,fetch
- 文件读写等涉及IO的操作
(二) 进程与线程
- 进程: 程序的运行状态, 执行实例
- 一个cpu同一时刻只能执行一个进程,通过上下文切换实现多任务,除非多核
- 线程: 进程中的某个任务,即一个进程,可以由多个线程完成
- js的特征,决定了它只能是单线程,例如dom操作中, 增删元素就不可能同时进行
- 单线程可确保js按用户要求的顺序执行,并确定业务逻辑正确,结果可控
- 但是单线程,也决定了所有任务必须在一个执行栈中完成,导致耗时任务必然会阻塞整个线程
- 解决文案: 任务队列与事件循环(事件轮询)
通信解释:http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html
(三) 单线程,任务队列,事件循环之间的关系与协同
- js所有任务都在主线程中”同步”执行
- 异步任务以回调的形式声明,并离开主线程,交给多线程的浏览器去执行
- 异步任务执行完毕,进入到”任务队列”中排队等待进入主线程执行
- 主线程同步任务全部完成后, 通过”事件循环”查询”任务队列”中是否有等待的任务
- 如果有,则该就绪任务进入主线程同步执行
- 该任务完成后, 事件循环会再次取出下一个就绪的异步任务进入主线程
- 以上过程不断重复, 直到主线程中的同步任务, 任务队列中的异步任务全部执行完毕
此图异步执行原理有误,个人理解
1.2 传统异步 xhr
- 创建对象:
new XMLHttpRequest()
;- 响应类型:
xhr.responseType = "json"
;- 配置参数:
xhr.open("GET", url, true)
;- 请求回调:
xhr.onload = () => console.log(xhr.response)
;- 失败回调:
xhr.onerror = () => console.log("Error")
;- 发起请求:
xhr.send(null)
;xhr
至少监听2个事件:load,error
, 调用2个函数:open
,send
post
请求,需要设置一下请求头与请求的数据,其它与get
请求完全相同
<button onclick="getUser1(this)">查询用户信息: XHR</button>
<script>
function getUser1(btn) {
//1. 创建对象
const xhr = new XMLHttpRequest();
//2. 响应类型
xhr.responseType = "json";
//3. 配置参数
// 不传get参数,默认返回全部用户, 用table显示
let url = "http://website.io/users.php";
// 如果有get参数,用户id,用ul列表显示
// url = "http://website.io/users.php?id=1";
xhr.open("GET", url);
//4. 请求回调
xhr.onload = () => {
console.log(xhr.response);
// 将数组渲染到页面中
render(xhr.response, btn);
};
//5. 失败回调
xhr.onerror = () => console.log("Error");
//6. 发起请求
xhr.send(null);
}
</script>
<script src="../js/function.js"></script>
1.3 fetch异步编程
语法:
fetch(url).them(getRes).them(handleRes).catch(err)
url
请求响应地址getRes
回调函数 返回jsonresponse.json()
handleRes
回调函数 得到JSON并处理err
处理异步执行的错误信息
fetch api
浏览器原生实现的异步技术axios
基于 xhr / fetchfetch
基于Promise, 返回 Promise对象
window.addEventListener("load", ()=>{
let url = "https://jsonplaceholder.typicode.com/users";
fetch(url)
.then((response) => response.json())
.then((json) => {
// 打印到控制台
console.log(json);
});
},false)
fetch异步编程语法糖
async
与await
// async:声明此函数是异步任务
window.addEventListener(
"load",
async () => {
let url = "https://jsonplaceholder.typicode.com/users";
// 异步耗时操作,需要等待结果才可进入下一步
const response = await fetch(url);
// 获取到结果之后,再转为json
const result = await response.json();
console.log(result);
},
false
);
2.模块成员导出与导入,别名使用场景与命名空间演示
模板导出
// 1.逐个导出
export let userName = "张三";
export let sex = "male";
// 2.统一导出
// (1)声明
let hello = "hello";
function getHello(hello, userName) {
return `${hello}:${userName}`;
}
// (2)导出: 对象字面量 {...}
export { hello, getHello };
// 3.别名导出
let prodName = "华为手机";
let price = 5000;
function prdoPrice(prodName, price) {
return `手机:${prodName} , 价格:${price}`;
}
export { prodName as n, price as p, prdoPrice as fn };
// 4.默认导出 只导出一个值 一个模块,只能有一个默认导出
export default class {
constructor(userName) {
this.userName = userName;
}
getUserName() {
return `Hello ${this.username}`;
}
}
// 5.混合导出 默认成员和非默认成员
// (1) 默认成员 第四点已导出
// (2) 非默认成员
let email = "1331440618@qq.com";
// 私有成员, 不对外 不导出
let userSex = "male";
export { email };
// 可以将邮箱 email 做为默认导出 , 起一个特殊的别名 default
// export {email as default};
模板导入
<!-- 默认script在浏览器环境下不支持模块,需要指定类型type="module" -->
<script type="module">
// 1.逐个(统一)导出 统一导入
import { userName, sex, hello, getHello } from "./modules/m1.js";
console.log(" 1.逐个(统一)导出 统一导入");
console.log(`**userName: ${userName} , sex: ${sex} **
${getHello(hello, userName)}`);
console.log(" ====================== ");
// 2.别名导出 统一导入
import { n, p, fn } from "./modules/m1.js";
console.log(" 2.别名导出 统一导入: ");
console.log(fn(n, p));
console.log(" ====================== ");
// 3.别名导出 别名导入
import { n as prodName, p as price, fn as prodPrice } from "./modules/m1.js";
console.log(" 3.别名导出 别名导入: ");
console.log(prodPrice(prodName, price));
console.log(" ====================== ");
// 4.默认导出 在导入时重命名
import getName from "./modules/m1.js";
console.log(" 4.默认导出 在导入时重命名:");
const getUserName = new getName("张三, 李四, 王二, 麻子");
console.log(getUserName);
console.log(" ====================== ");
// 5.混合导入 默认导出重命名在前 后面跟上模板字面量
import Name, { email } from "./modules/m1.js";
console.log(" 5.混合导入 默认导出重命名在前:");
console.log(new Name("张三, 李四"));
console.log(email);
console.log(" ====================== ");
// 6.命名空间导入 把所有模块封装在命名空间中 用属性“.”的方式来使用
import * as info from "./modules/m1.js";
console.log(" 6.命名空间导入 ");
console.log(info.userName);
console.log(new info.default("李四, 张三"));
</script>