Blogger Information
Blog 29
fans 1
comment 0
visits 14840
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
异步之xhr与fecth的优化与模块
风车
Original
517 people have browsed it

同步与异步

同步:顺序执行,优点是静态预判结果可控, 缺点就是如果某一步任务执行慢,那就会影响到后续的所有代码
异步:乱序执行,优点就是不会阻塞代码,但是缺点也很明显就是顺序不可控

以银行排队办业务为例

  1. * 1. 同步: 默认排队叫号, 依次办理
  2. * 2. 异步: 耗时任务(如修改密码忘带身份证)则离开队列, 后面任务继续
  3. * 3. 任务队列: 取了身份证回来了, 就待在"任务队列"中等待再次叫号

哪些是异步任务(耗时)?

  1. * 1. 定时任务: setTimeout, setInterval
  2. * 2. 事件监听: addEventListener
  3. * 3. 网络请求: ajax, promise,fetch
  4. * 4. 文件读写等涉及IO的操作

(二) 进程与线程

  1. * 1. 进程: 程序的运行状态, 执行实例
  2. * 2. 一个cpu同一时刻只能执行一个进程,通过上下文切换实现多任务,除非多核
  3. * 3. 线程: 进程中的某个任务,即一个进程,可以由多个线程完成
  4. * 4. js的特征,决定了它只能是单线程,例如dom操作中, 增删元素就不可能同时进行
  5. * 5. 单线程可确保js按用户要求的顺序执行,并确定业务逻辑正确,结果可控
  6. * 6. 但是单线程,也决定了所有任务必须在一个执行栈中完成,导致耗时任务必然会阻塞整个线程
  7. * 7. 解决文案: 任务队列与事件循环(事件轮询)

比较通俗的解释:http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html

(三) 单线程,任务队列,事件循环之间的关系与协同

  1. * 1. js所有任务都在主线程中"同步"执行
  2. * 2. 异步任务以回调的形式声明,并离开主线程,交给多线程的浏览器去执行
  3. * 3. 异步任务执行完毕,进入到"任务队列"中排队等待进入主线程执行
  4. * 4. 主线程同步任务全部完成后, 通过"事件循环"查询"任务队列"中是否有等待的任务
  5. * 5. 如果有,则该就绪任务进入主线程同步执行
  6. * 6. 该任务完成后, 事件循环会再次取出下一个就绪的异步任务进入主线程
  7. * 7. 以上过程不断重复, 直到主线程中的同步任务, 任务队列中的异步任务全部执行完毕
  8. */

下面的代码按正常顺序会输出100 .200 .300 但是因为输出200的地方是一个定时器,也就是异步任务,因为在JS中会先执行同步任务,在执行异步任务 所以会先输出100.300 最后再输出200.

  1. console.log(100);
  2. setTimeout('console.log(200)', 1000);
  3. console.log(300);

ajax-xhr编程步骤

传统方式,信息会在新页面中或者弹窗中查看,用户体验不好 —>
Ajax: 局部更新,用户不需要离开当前页面就可以看到新内容, 单页面应用SPA(基于异步任务)
传统XHR的编程步骤
**

  1. * 1. 创建对象: new XMLHttpRequest();
  2. * 2. 响应类型: xhr.responseType = "json";
  3. * 3. 配置参数: xhr.open("GET", url, true);
  4. * 4. 请求回调: xhr.onload = () => console.log(xhr.response);
  5. * 5. 失败回调: xhr.onerror = () => console.log("Error");
  6. * 6. 发起请求: xhr.send(null);
  7. *
  8. * xhr 至少监听2个事件: load,error, 调用2个函数: open,send
  9. * post请求,需要设置一下请求头与请求的数据,其它与get请求完全相同
  10. */

下面是实例演示
在页面创建两个按钮并创建点击事件

  1. <button onclick="getUser1(this)">查询用户信息: XHR</button>
  2. <button onclick="getUser2(this)">查询用户信息: Fetch</button>

然后根据上面的步骤一步一步创建xhr

  1. function getUser1(btn) {
  2. //1. 创建对象
  3. const xhr = new XMLHttpRequest();
  4. //2. 响应类型
  5. xhr.responseType = 'json';
  6. //3. 配置参数
  7. // 不传get参数,默认返回全部用户, 用表格
  8. let url = 'http://website.io/users.php';
  9. // 如果有get参数,就是用户id,输入对应的id 获取对应的值
  10. url = 'http://website.io/users.php?id=1';
  11. //url:当前的服务器地址
  12. xhr.open('GET', url);
  13. //4. 请求回调
  14. xhr.onload = () => {
  15. console.log(xhr.response);
  16. // 将数组渲染到页面中
  17. render(xhr.response, btn);
  18. };
  19. //5. 失败回调
  20. xhr.onerror = () => console.log('Error');
  21. //6. 发起请求
  22. xhr.send(null);
  23. }

fetch请求语法
fetch: 基于Promise, 返回 Promise对象

  1. // fetch
  2. /**
  3. * fetch(url) //当前请求的地址
  4. * .then(res) //then(然后的意思)链式执行,一步一步的写
  5. * .then(...)
  6. * .catch(err)
  7. */
  8. function getUser2(btn) {
  9. let url = 'http://website.io/users.php';
  10. // fetch的所有操作都是异步的,但是通过then使用顺序可控
  11. fetch(url)
  12. .then(function (response) {
  13. return response.json();
  14. })
  15. .then(function (json) {
  16. // 控制台
  17. console.log(json);
  18. // 将数组渲染到页面中
  19. render(json, btn);
  20. });
  21. }

现在推荐使用fetch请求,更加简便明了

fetch 请求中的api(浏览器原生实现的异步技术,可以直接用)
fetch: 基于Promise, 返回 Promise对象
随机生成json数据的网站:http://jsonplaceholder.typicode.com/users
通过上方的网站可以生成一组假的JSON数据
然后通过上方网站提供的代码访问拿到其中的第一条数据(以列表的形式渲染到页面)
(路径最后的todos/1 表示第一条数据)
这里如果直接访问会出现跨域的错误,需要在php中进行处理

  1. fetch('https://jsonplaceholder.typicode.com/todos/1')
  2. .then(response => response.json())
  3. .then(json => console.log(json))

如果我们想拿到所有的一组数据,就将路径直接改为/users(如果请求一组数据,会以表格的形式渲染到页面)

  1. fetch('https://jsonplaceholder.typicode.com/users')
  2. .then(response => response.json())
  3. .then(json => console.log(json));

上方的代码就是请求数据接口的代码,后期可以直接将需要访问的接口网址直接替换进去

出现跨域请求是因为同源策略

同源策略要求:协议相同,域名相同,端口相同,否则就是跨域请求,这是禁止的(这个禁止是脚本禁止,标签的话一般不受影响)
js脚本禁止跨域是因为js脚本可以动态的进行dom操作,对网站进行增删改查等,这样又很大的安全隐患

  1. //写在PHP里面,表示允许所有跨域请求
  2. header("access-control-allow-origin: *");

创建一个按钮。点击之后会返回对应的数据,其实就是创建一个点击事件,点击之后会通过上面的方法,访问php数据,然后传回并渲染到页面

  1. function getUser(btn) {
  2. let url = 'http://website.io/users.php?id=1';
  3. fetch(url)
  4. .then(response => response.json())
  5. .then(json => {
  6. console.log(json);
  7. // 将数组渲染到页面中
  8. render(json, btn);
  9. });

ECMA2017中, 有async,await,用来简化异步回调方法
首先在上方对象的前面加上一个async关键字(这个关键字的意思是,这是一个异步操作)
然后在下面创建一个常量接收到数据,在fetch之前加上 await关键字(这个关键字只能在异步操作中使用)

  1. async function getUser(btn) {
  2. let url = 'http://website.io/users.php';
  3. // 这是一个异步耗时操作,需要等待结果才可进入下一步
  4. const response = await fetch(url);
  5. // 获取到结果之后,再转为json
  6. const result = await response.json();
  7. console.log(result);
  8. // 将数组渲染到页面中
  9. render(result, btn);
  10. }

模块

模块成员的导出与导入
js中, 所有代码共用一个全局作用域: window,有时候会产生一些冲突,在ES6中,可以通过模块的来解决这个问题
1.一个模块就是一个JS文档
2.模块有自己独立的作用域
3.模块内的成员,默认全部私有,只有导出才能被外部使用
导出与导入
1.逐个导出:

  1. //第一个方法:在变量前面加上export
  2. export let username = '猪老师';
  3. //第二种方法:也是添加export 只是这是导出一个函数
  4. export function hello(username) {
  5. return `Hello, ${username}`;
  6. }

导入模块
通过import关键字导入
默认script在浏览器环境下不支持模块,需要指定type

  1. <script type="module">
  2. // js中, 所有代码共用一个全局作用域: window
  3. // 导入模块
  4. //import 导入模板中的变量或函数,需要用模板字面量来接收,这个模板字面量的名称要与模板中定义的变量或者函数名一致
  5. import { username, hello } from './modules/m1.js';
  6. console.log(username);
  7. console.log(hello(username));
  8. </script>

2.逐个导出

  1. // (1) 声明
  2. let username = '猪老师';
  3. function hello(username) {
  4. return `Hello, ${username}`;
  5. }
  6. // (2) 导出: 对象字面量 {...}
  7. export { username, hello };

在导出或者导入时,可能会出现命名重复,这时候可以使用别名导出

  1. let username = '欧阳老师';
  2. function hello(username) {
  3. return `Hello, ${username}`;
  4. }
  5. // (2) 别名导出: 隐藏模块成员的真实名称, as
  6. export { username as myname, hello as getName };

在导出的时候通过as关键字,为需要到导出的数据定义一个别名,同样的,在使用别名后,导入使用时也需要使用别名导入

  1. <script type="module">
  2. import { myname,getName } from './modules/m2.js';
  3. console.log(myname);
  4. console.log(getName(myname));
  5. </script>

如果导入时 导出时定义的别名还是已经被定义了,还是可以在导入时使用as关键字再次重命名,然后调用时需要用最新的名字

  1. <script type="module">
  2. import { myname as name,getName as get } from './modules/m2.js';
  3. console.log(name);
  4. console.log(get(name));
  5. </script>

默认导出
一个模块,只能有一个默认导出
默认导出,导出的是一个值,没有命名,由导出的时候再命名

  1. 定义一个类,然后直接导出
  2. export default class {
  3. constructor(username) {
  4. this.username = username;
  5. }
  6. getUsername() {
  7. return `Hello, ${this.username}`;
  8. }
  9. }

默认导入

  1. <script type="module">
  2. // 导入模块的默认成员不要用对象字面量,用标识符
  3. // 只要不用对象字面量接收模块成员,就是默认导出
  4. import User from './modules/m3.js';
  5. //因为默认导出的是一个类,所以需要new一个 再赋值输出
  6. const user = new User('王老师');
  7. console.log(user.username);
  8. console.log(user.getUsername(user.username));
  9. </script>

混合导出(默认成员与非默认成员的导出)

  1. // 默认成员与非默认成员的导出
  2. let username = '牛老师';
  3. function hello(username) {
  4. return `Hello, ${username}`;
  5. }
  6. let useremail = 'admin@php.cn';
  7. // 私有成员, 不对外
  8. let sex = 'male';
  9. // 将邮箱 useremail 做为默认导出 , 起一个特殊的别名 default
  10. export { username, hello, useremail as default };

混合导入

  1. <script type="module">
  2. // 接收混合导入成员,默认成员的命名,必须放在非默认的前面
  3. //默认导入是可以随便命名,因为 默认导出时没有命名
  4. import email, { username, hello } from './modules/m4.js';
  5. console.log(email);
  6. console.log(hello(username));
  7. </script>

命名空间
导入的成员,过多时,使用{…}会很长的
可以将所有的导入成员,全部挂载到一个对象上
此时, 导入模块的成员,自动成为该对象上的属性
这个对象就是: 命名空间
注释:在导入时候 用 * (命名空间名) 将所有的对象全部挂载到同一个新的命名空间里面

  1. import * as user from './modules/m4.js';
  2. console.log(user);

如何访问这个新命名空间里面的成员
在访问命名空间的模块成员是,直接添加命名空间前缀

  1. console.log(user.username);
  2. console.log(user.hello(user.username));
Correcting teacher:PHPzPHPz

Correction status:qualified

Teacher's comments:
Statement of this Website
The copyright of this blog article belongs to the blogger. Please specify the address when reprinting! If there is any infringement or violation of the law, please contact admin@php.cn Report processing!
All comments Speak rationally on civilized internet, please comply with News Comment Service Agreement
0 comments
Author's latest blog post