2.Javascript 异步编程

Javascript 异步编程

主要包含以下几个内容:

  • 同步和异步的概念
  • 实现异步的几种方式
    • 回调函数
    • promise
    • 生成器和迭代器
    • ES7 async 和 await

1. 同步和异步的概念

在计算机中,所谓同步,就是指同一时间只能做一件事情。比如看下面的代码:

console.log("Hello");
console.log("world");
console.log("F69");

同步任务的特点在于,一次执行一个任务,后面的任务只能排队,只有等待前面的任务结束之后,才能执行下一个同步任务。

所谓异步,有两种解决方案:1. 多线程 2. 单线程非阻塞

所谓单线程非阻塞,就是指将耗时间的任务(异步任务)交给其他模块来进行处理,处理完成之后,再由js的单线程进行处理。

整个js引擎的同步异步任务执行顺序如下:

  1. 执行同步任务
  2. 如果遇到异步任务,交给异步模块
  3. 如果异步处理模块已经处理异步任务,扔到任务队列里面排队,因为必须要等到所有的同步任务执行完之后,才会执行异步任务的结果
  4. 所有的同步任务执行完成,从任务队列里面获取异步的执行结果

常见的异步任务有:

setTimeout / setInterval、文件的读写(I/O 操作),网络请求

2. 实现异步的几种方式

有些时候,我们需要等待某一个异步操作结束之后,再执行下一个步骤。

举例如下:每隔2秒输出一个数,下面的代码是不正确的:

setTimeout(function(){
    console.log(1);
},2000);
setTimeout(function(){
    console.log(2);
},2000);
setTimeout(function(){
    console.log(3);
},2000);

因为上面的3个setTimeout都是异步代码,都会被放到异步处理模块,而且由于时间都是2秒,几乎同时完成,然后被放入到任务队列。

如果要解决上面的问题,可以采取以下的几种方案:

  • 回调函数
  • promise
  • 生成器
  • async await

首先我们来看一下回调函数的处理方式,代码如下:

setTimeout(function () {
    console.log(1);
    setTimeout(function () {
        console.log(2);
        setTimeout(function () {
            console.log(3);
        }, 2000);
    }, 2000);
}, 2000);

读取文件的例子:

const fs = require('fs'); // 引入 fs 模块
fs.readFile('./file1.txt','utf8',function(err,data){
    if(err) throw err;
    console.log(data);
    fs.readFile('./file2.txt','utf8',function(err,data){
        if(err) throw err;
        console.log(data);
        fs.readFile('./file3.txt','utf8',function(err,data){
            if(err) throw err;
            console.log(data);
        });
    });
});

使用回调来获取网络请求的示例:

let classID, teacherId;
let xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
xhr.open("get", "./stu.json");
xhr.send();
xhr.onreadystatechange = function () {
  if (xhr.readyState == 4 && xhr.status == 200) { // 服务器正常的响应数据回来
    let arr = JSON.parse(xhr.responseText).student;
    for (let i = 0; i < arr.length; i++) {
      if (arr[i].name === "韩梅梅") {
        classID = arr[i].classId;
      }
    }
    // 继续发送请求
    xhr.open("get", "./classes.json");
    xhr.send();
    xhr.onreadystatechange = function () {
      if (xhr.readyState == 4 && xhr.status == 200) { // 服务器正常的响应数据回来
        let arr = JSON.parse(xhr.responseText).classes;
        for (let i = 0; i < arr.length; i++) {
          if (arr[i].id === classID) {
            teacherId = arr[i].teacherId;

          }
        }
        // 继续发送请求
        xhr.open("get", "./teacher.json");
        xhr.send();
        xhr.onreadystatechange = function () {
          if (xhr.readyState == 4 && xhr.status == 200) { // 服务器正常的响应数据回来
            let arr = JSON.parse(xhr.responseText).teachers;
            for (let i = 0; i < arr.length; i++) {
              if (arr[i].id === teacherId) {
                console.log(arr[i].name)
              }
            }
            // 继续发送请求
          }
        }
      }
    }
  }
}

但是,使用回调有一个最大的问题,就是会带来回调地狱(callback hell)

所谓回调地狱,就是指代码在可读性上面非常的差,代码逐渐的在往右边延伸。例如:

setTimeout(function () {
    console.log(1);
    setTimeout(function () {
        console.log(2);
        setTimeout(function () {
            console.log(3);
            setTimeout(function () {
                console.log(4);
                setTimeout(function () {
                    console.log(5);
                    setTimeout(function () {
                        console.log(6);
                        setTimeout(function () {
                            console.log(7);
                            setTimeout(function () {
                                console.log(8);
                            }, 2000);
                        }, 2000);
                    }, 2000);
                }, 2000);
            }, 2000);
        }, 2000);
    }, 2000);
}, 2000);

随着 ES6 的到来,出现了解决方案,promise。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值