Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

异步

1. Promise、then

ES6 中引入,解决了 ES5 地狱回调的问题

同步和异步的概念

  • 同步:代码从上往下执行,遇到同步,就会一直等,等到完成才会向下走

  • 异步:代码从上往下执行,遇到异步,就先走了,等到异步发出完成信号,才回来处理(调用相应回调函数)

【例子】

三次 ajax 异步回调,依次访问 1,2,3 的 url,成功后才访问下一个

  • 地狱回调

    $.ajax({
      url: "1",
      success: function (res) {
        console.log("结果1" + res);
        $.ajax({
          url: "2",
          success: function (res) {
            console.log("结果2" + res);
            $.ajax({
              url: "3",
              success: function (res) {
                console.log("结果3" + res);
              },
            });
          },
        });
      },
    });
    
  • Promise 例子

    let p1 = new Promise(function(resolve,reject){
        $.ajax({
            url:"1",
            success:function(res){
                resolve(res)
                //成功后发出信号,携带res内容,就会触发下面then之后的回调
            }
        })
    }
    
    p1
    .then(function(res){
        console.log("结果1" + res);
        let p2 = new Promise(跟上面一样写,也是成功后发出信号);
        return p2; //记得返回,作为下一个then函数的执行对象
    })
    .then(function(res){
        console.log("结果2" + res);
        let p3 = new Promise(跟上面一样写,也是成功后发出信号);
        return p3; //记得返回,作为下一个then函数的执行对象
    })
    .then(function(res){
        console.log("结果3" + res);
    })
    
    • 通过 new 一个 Promise 对象,把每一次的异步调用都封装起来,等到发出 resolve 或者 reject 信号,才执行对象的 then 方法,代码变得很优雅!

还支持 all 和 race

  • all:多个 Promise 都触发信号,才能往下执行 then 方法

    .then(function(res){
        let p4 = new Promise()
        let p5 = new Promise()
        return Promise.all([p4,p5])
    })
    .then()
    
  • race: 只要有一个触发信号,就能往下执行 then 方法

    .then(function(res){
        let p6 = new Promise()
        let p7 = new Promise()
        return Promise.race([p6,p7])
    })
    .then()
    

在 Promise 的协助下,就能封装出 axios

  • ajax 缺点

    • 本身针对 MVC 编程,不符合现在 MVVM 的浪潮
    • 基于原生 XHR 开发,架构不清晰,已经有了 fecth 的替代方案
    • 来自 jquery,只用到 ajax 却要引入整个库,不划算
  • axios 改进点

    • 提供了一些并发请求接口
      axios.all([axios.get("data.json"), axios.get("city.json")]).then();
      

2. async、await

ES7 中引入,让 ES6 的 promise 可读性更强

async 是什么?

  • 声明会返回Promise的函数

  • 两句话

    • async 就是声明“后面定义的函数”为异步函数
    • 这样才能在别的 async 函数体里面调用这个异步函数,并在它前面使用 await 去等待 Promise 兑现
//ES6写法
function s() {
  return Promise.resolve("TEST");
}

function f() {
  return Promise.reject("Error");
}
//ES7写法
async function asyncS() {
  return "TEST";
}

async function asyncF() {
  throw "Error";
}

本质就是 Generator 函数的语法糖

await 是什么?

只能在声明了是 async 的函数内使用 让我们能等待一个 Promise 返回信号,返回后才能继续往下执行

【例子】

  • 模拟异步

    • 用 setTimeout 模拟异步,并用 Promise 封装
      let func = (val) => {
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            resolve(val);
          }, 1000);
        });
      };
      
  • 定义异步函数

    • 下面调用 func 会返回一个 Promise 对象,可以加 await 等待
      async function f() {
        const response = await func("http://example.com/");
        //下面就相当于Promise返回后then里面的回调,使得异步代码能够用同步逻辑去书写
        console.log(response);
      }
      
  • 执行

    • 由于在最外层不能用 await,所以需要用下面两种方法
    • 【办法 1】用.then 写 Promise 返回后的回调
      f().then(() => console.log("Finished"));
      
    • 【办法 2】用立即执行函数
      (async () => {
        await f();
        console.log("Finished");
      })();
      
  • 如何对 await 捕获异常

    • 使用try catch,包裹住 await 函数 即可

Generator 函数是什么?

  • 它可以断续执行,并返回一连串的值

  • 函数内书写一行行的“yeild 返回值”来定义返回(每一行可返回一次),并停止执行

    加上星号来定义;
    function* helloWorldGenerator() {
      yield "hello";
      yield "world";
      return "ending";
    }
    
  • 调用此函数生成对象,再调用对象的.next方法来恢复执行,并触发一次 yield 返回

    • 生成对象
      var hw = helloWorldGenerator();
      
    • 调用对象中的 next 方法
      console.log(hw.next()); //返回{value:"hello",done:false}
      
      • 这里
        • value 代表 yield 返回的值
        • done 代表还可以继续调用.next()得到返回值
    • 再次调用
      console.log(hw.next()); //返回{value:"world",done:false}
      
    • 再次调用
      console.log(hw.next()); //返回{value:"ending",done:true}
      
      • 发现 done 为 true,即可知道生成器执行到最后的 return 了

3. JSONP

  • 原理

    • 在 DOM 中添加一个 script 标签,标签会向其 src 请求一个脚本
    • 返回的脚本含有“调用本地的某个函数”的代码,从而实现异步
  • 例子

    function jsonpFunc(data) {
      console.log(data);
    }
    
    function request_jsonp() {
      var script = document.createElement("script");
      script.type = "text/javascript";
      script.src = "https://api.xxx.com/jsonp?callback=jsonpFunc";
      document.head.appendChild(script);
    }
    
    request_jsonp();
    
    • 会返回以下脚本,给予参数并调用本地函数
      jsonpFunc({
        name: "Tom",
      });
      
  • 特点

    • 可以跨域

4. setTimeout

  • 原理

    • 依赖事件循环,在一定时间后执行回调函数
  • 例子

    setTimeout(() => {
      console.log("delay 1 second");
    }, 1000);
    
    • 注意单位是毫秒