• ④ ES6 异步编程与模块化


    1 异步操作必备知识

    1.1 异步操作前置知识

    1. js 是单线程的

    2. 同步任务与异步任务

    3. ajax 原理

    • 前后端数据分离 前端 <-> 后端 ajax

    4. callback hell

    • 回调地狱

    2 Ajax 原理与 Callback Hell

    2.1 ajax 原理

    一种前后端的交互方式,可实现页面的部分刷新

    1. 创建 XMLHttpRequest 对象

    2. 发送请求

    3. 服务端响应

    function ajax(url, cb) {
      // 1. 创建XMLHttpRequest对象
      var xmlhttp
      if(window.XMLHttpRequest) {
        xmlhttp = new XMLHttpRequest()
      } else {  // IE5 IE6
        xmlhttp = new ActiveXObject('Microsoft.XMLHTTP')
      }
      // 2. 发送请求
      xmlhttp.open('GET', url, true)
      xmlhttp.send()
      // 3. 服务端响应
      xmlhttp.onreadystatechange = function() {
        if(xmlhttp.readyState === 4 && xmlhttp.status === 200) {
          var obj = JSON.parse(xmlhttp.responeText)
          console.log(obj);
          cb(obj)
        }
      }
    }
    var url = 'http://musicapi.xiecheng.live/personalized'
    ajax(url, res => {
      console.log(res);
    })
    

    2.2 callback hell

    • 回调地狱
    //  1 -> 2 -> 3
    ajax('static/a.json', res => {
      console.log(res);
      ajax('static/b.json', res => {
        console.log(res);
        ajax('static/c.json', res => {
          console.log(res);
        })
      })
    })
    

    3 异步编程解决方案 Promise 的基本用法

    // 状态管理
    // resolve 成功
    // reject 失败
    let p = new Promise((resolve, reject) => {
      setTimeout(() => {
        console.log('imooc');
        // resolve('成功')
        reject('失败')
      }, 1000);
    }).then(res => {
      console.log(res);
    }, err => {
      console.log(err);
    })
    

    3.1 promise 是同步执行的

    • then 是异步执行的(微任务)
    let p = new Promise((resolve, reject) => {
      console.log(1);
      resolve(3)
    })
    console.log(2);
    p.then(res => {
      console.log(res);
    })
    // 1 2 3
    

    3.2 Promise 的三种状态

    • pending

    • fulfilled

    • rejected

    let p1 = new Promise((resolve, reject) => {
      resolve(1)
    })
    let p2 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(2)
      }, 1000);
    })
    let p3 = new Promise((resolve, reject) => {
      setTimeout(() => {
        reject(3)
      }, 1000);
    })
    console.log(p1); // fulfilled
    console.log(p2); // pending
    console.log(p3); // pending
    setTimeout(() => {
      console.log(p2); // fulfilled
    }, 2000);
    setTimeout(() => {
      console.log(p3); // rejected
    }, 2000);
    
    p1.then(res => {
      console.log(res); // 1
    })
    p2.then(res => {
      console.log(res); // 2
    })
    p3.catch(err => {
      console.log(err); // 3
    })
    
    // fulfilled pending pending 1 2 3 fulfilled rejected
    

    3.3 Promise 的状态变化是不可逆的

    • pending -> fulfilled

    • pending -> rejected

    let p = new Promise((resolve, reject) => {
      resolve(1)
      reject(2)
    })
    p.then(res => {
      console.log(res);
    }, err => {
      console.log(err);
    })
    // 1
    

    3.4 使用 Promise 解决回调地狱

    function ajax(url, cb) {
      // 1. 创建XMLHttpRequest对象
      var xmlhttp
      if(window.XMLHttpRequest) {
        xmlhttp = new XMLHttpRequest()
      } else {  // IE5 IE6
        xmlhttp = new ActiveXObject('Microsoft.XMLHTTP')
      }
      // 2. 发送请求
      xmlhttp.open('GET', url, true)
      xmlhttp.send()
      // 3. 服务端响应
      xmlhttp.onreadystatechange = function() {
        if(xmlhttp.readyState === 4 && xmlhttp.status === 200) {
          var obj = JSON.parse(xmlhttp.responseText)
          // console.log(obj);
          cb(obj)
        }
      }
    }
    
    • 直白代码编写
    new Promise((resolve, reject) => {
      ajax('static/a.json', res => {
        console.log(res);
        resolve()
      })
    }).then(res => {
      console.log('a成功');
      // new Promise((resolve, reject) => {
      return new Promise((resolve, reject) => {
        ajax('static/b.json', res => {
          console.log(res);
          resolve()
        })
      })
    }).then(res => {
      console.log('b成功');
      return new Promise((resolve, reject) => {
        ajax('static/c.json', res => {
          console.log(res);
          resolve()
        })
      })
    }).then(res => {
      console.log('c成功');
    })
    
    
    • 代码简化
    function getPromise(url) {
      return new Promise((resolve, reject) => {
        ajax(url, res => {
          resolve(res)
        })
      })
    }
    getPromise('static/a.json')
      .then(res => {
        console.log(res);
        return getPromise('static/b.json')
      }).then(res => {
        console.log(res);
        return getPromise('static/c.json')
      }).then(res => {
        console.log(res);
      })
    

    3.5 Promise 失败状态处理

    function ajax(url, successCallback, failCallback) {
      // 1. 创建XMLHttpRequest对象
      var xmlhttp
      if(window.XMLHttpRequest) {
        xmlhttp = new XMLHttpRequest()
      } else {  // IE5 IE6
        xmlhttp = new ActiveXObject('Microsoft.XMLHTTP')
      }
      // 2. 发送请求
      xmlhttp.open('GET', url, true)
      xmlhttp.send()
      // 3. 服务端响应
      xmlhttp.onreadystatechange = function() {
        if(xmlhttp.readyState === 4 && xmlhttp.status === 200) {
          var obj = JSON.parse(xmlhttp.responseText)
          // console.log(obj);
          successCallback && successCallback(obj)
        } else if(xmlhttp.readyState === 4 && xmlhttp.status === 404) {
          failCallback && failCallback(xmlhttp.statusText)
        }
      }
    }
    
    function getPromise(url) {
      return new Promise((resolve, reject) => {
        ajax(url, res => {
          resolve(res)
        }, err => {
          reject(err)
        })
      })
    }
    

    1. aa 读取失败,不影响后续 then 函数的执行

    getPromise('static/aa.json')
      .then(res => {
        console.log(res);
        return getPromise('static/b.json')
      }, err => {
        console.log(err); // Not Found
        // return getPromise('static/b.json')
      }).then(res => {
        console.log(res); // undefined // b,我是b
        return getPromise('static/c.json')
      }).then(res => {
        console.log(res); // c,我是c
      })
    

    2. 遇到失败,直接进入 catch 不进入后续 then

    getPromise('static/aa.json')
      .then(res => {
        console.log(res);
        return getPromise('static/b.json')
      }).then(res => {
        console.log(res);
        return getPromise('static/c.json')
      }).then(res => {
        console.log(res);
      }).catch(err => {
        console.log(err); // Not Found
      })
    
    

    4 Promise 的静态方法

    .then() .catch() 需要通过 new Promise 去调用,是实例方法

    静态方法是通过 Promise.名称() 调用的

    4.1 Promise.resolve() Promise.reject()

    • 返回值:Promise 对象

    • 使用场景:当前没有 Promise 实例但要继续调用 then

    let p1 = Promise.resolve('success')
    console.log(p1); // success
    p1.then(res => {
      console.log(res); // success
    })
    
    let p2 = Promise.reject('fail')
    console.log(p2); // 无catch报错 有catch->fail
    p2.catch(err => {
      console.log(err); // fail
    })
    
    function foo(flag) {
      if(flag) {
        return new Promise(resolve => {
          resolve('success')
        })
      } else {
        // return Promise.resolve('fail')
        return Promise.reject('fail')
      }
    }
    foo(true).then(res => {
      console.log(res); // success
    })
    foo(false).then(res => {
      // console.log(res); // fail
    }, err => {
      console.log(err); // fail
    })
    

    4.2 Promise.all() Promise.race()

    1. Promise.all()

    • 返回值:一个新的 Promise 对象

      • 只有当 Promise 所有的参数对象都成功时才会触发成功

      • 一旦 Promise 里的任意参数对象失败都会触发失败

        • 错误信息:第一个触发失败的参数对象的错误信息
    let p1 = new Promise((resolve, reject) => {
      setTimeout(() => {
        console.log(1);
        resolve('1success')
      }, 1000);
    })
    let p2 = new Promise((resolve, reject) => {
      setTimeout(() => {
        console.log(2);
        // resolve('2success')
        reject('2fail')
      }, 2000);
    })
    let p3 = new Promise((resolve, reject) => {
      setTimeout(() => {
        console.log(3);
        resolve('3success')
      }, 3000);
    })
    Promise.all([p1, p2, p3]).then(res => {
      console.log(res); // resolve('2success') ->1 2 3 ['1success', '2success', '3success']
    }, err => {
      console.log(err); // reject('2fail') -> 1 2 ['2fail'] 3
    })
    
    应用
    • 同时上传 n 张图片
    const imgArr = ['1.jpg', '2.jpg', '3.jpg']
    let promiseArr = []
    imgArr.forEach(item => {
      promiseArr.push(new Promise((resolve, reject) => {
        // 图片上传操作
        resolve()
      }))
    })
    Promise.all(promiseArr).then(res => {
      // 插入数据库的操作
      console.log('图片全部上传完成', res); // 图片全部上传完成 (3) [undefined, undefined, undefined]
    })
    

    2. Promise.race()

    • 返回值:一个新的 Promise 对象

      • 一旦参数对象中的某个 promise 解决或拒绝,返回的 promise 就会解决或拒绝(速度最快的那个)
    let p1 = new Promise((resolve, reject) => {
      setTimeout(() => {
        console.log(1);
        // resolve('1success')
        reject('1fail')
      }, 1000);
    })
    let p2 = new Promise((resolve, reject) => {
      setTimeout(() => {
        console.log(2);
        resolve('2success')
      }, 2000);
    })
    let p3 = new Promise((resolve, reject) => {
      setTimeout(() => {
        console.log(3);
        resolve('3success')
      }, 3000);
    })
    Promise.race([p1, p2, p3]).then(res => {
      console.log(res); // resolve('1success') -> 1 1success 2 3
    }, err => {
      console.log(err); // reject('1fail') -> 1 1fail 2 3
    })
    
    
    应用
    • 在2s内未能加载图片则报请求超时
    function getImg() {
      return new Promise((resolve, reject) => {
        let img = new Image()
        img.onload = function() {
          resolve(img)
        }
        // img.src = 'http://www.xx.com/xx.jpg'
        img.src = 'http://www.imooc.com/static/img/index/logo.png'
      })
    }
    function timeout() {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          reject('图片请求超时')
        }, 2000);
      })
    }
    Promise.race([getImg(), timeout()]).then(res => {
      console.log(res); // <img src="http://www.imooc.com/static/img/index/logo.png">
    }, err => {
      console.log(err); // 图片请求超时
    })
    

    5 异步编程解决方案 Generator

    5.1 generator 函数

    1. generator 函数不会立即执行,而是生成迭代器对象

    2. 迭代器函数调用 next 语句,会执行到 yeild 语句为止

    3. 再次调用 next,会从当前 yeild 语句之后继续执行

    function* foo() {
      for(let i = 0; i < 3; i++) {
        yield i
      }
    }
    
    let f = foo()
    console.log(f.next()); // {value: 0, done: false}
    console.log(f.next()); // {value: 1, done: false}
    console.log(f.next()); // {value: 2, done: false}
    console.log(f.next()); // {value: undefined, done: true}
    

    注意

    1. generator 函数不能作为构造函数

    2. yeild 关键字只能在 generator 函数内使用

    function* gen (args) {
      args.forEach(item => {
        yield item + 1 // 报错
      })
    }
    

    5.2 generator 函数的返回值

    • value 表示当前这一次 yeild 后面的返回值

    • done 表示后续是否还有 yeild 语句

    5.3 next generator 是如何执行的

    function* gen (x) {
      let y = 2 * (yield(x + 1)) // x+1->6  yield(x + 1)->undefined
      let z = yield(y / 3)
      return x + y + z
    }
    let g = gen(5)
    console.log(g.next()); // {value: 6, done: false}
    console.log(g.next()); // {value: NaN, done: false}
    console.log(g.next()); // {value: NaN, done: false}
    

    next 是如何传参的

    function* gen (x) {
      let y = 2 * (yield(x + 1)) // x+1->6  yield(x+1)->12 y=24
      let z = yield(y / 3) // 24/3->8 yield(y/3)->13 z=13
      return x + y + z // 5+13+24=42
    }
    let g = gen(5)
    console.log(g.next()); // {value: 6, done: false}
    console.log(g.next(12)); // {value: 8, done: false}
    console.log(g.next(13)); // {value: 42, done: false}
    

    5.4 应用

    1. 数7

    • 可以暂停,让函数执行不会陷入死循环
    function* count(x = 1) {
      while(true) {
        if(x % 7 === 0) {
          yield x
        }
        x++
      }
    }
    let n = count()
    console.log(n.next().value);
    console.log(n.next().value);
    

    2. 解决回调地狱

    function ajax(url, callback) {
      var xmlhttp
      if(window.XMLHttpRequest) {
        xmlhttp = new XMLHttpRequest()
      } else {  // IE5 IE6
        xmlhttp = new ActiveXObject('Microsoft.XMLHTTP')
      }
      xmlhttp.open('GET', url, true)
      xmlhttp.send()
      xmlhttp.onreadystatechange = function() {
        if(xmlhttp.readyState === 4 && xmlhttp.status === 200) {
          var obj = JSON.parse(xmlhttp.responseText)
          callback(obj)
        }
      }
    }
    function request(url) {
      ajax(url, res => {
        getData.next(res)
      })
    }
    function* gen() {
      let res1 = yield request('static/a.json')
      console.log(res1);
      let res2 = yield request('static/b.json')
      console.log(res2);
      let res3 = yield request('static/c.json')
      console.log(res3);
    }
    let getData = gen()
    getData.next()
    

    6 迭代器 Iterator

    • Iterator 是一种接口机制,为各种不同的数据结构提供统一访问的机制

    • 主要供 for..of 消费

    • 一句话:不支持遍历的数据结构“可遍历”

    6.1 Iterator 遍历器

    • 指针对象
    function makeIterator(arr) {
      let nextIndex = 0
      return {
        next() {
          return nextIndex < arr.length ? {
            value: arr[nextIndex++],
            done: false
          } : {
            value: undefined,
            done: true
          }
        }
      }
    }
    let it = makeIterator(['a', 'b', 'c'])
    console.log(it.next());
    console.log(it.next());
    console.log(it.next());
    console.log(it.next());
    

    6.2 原生具备 Iterator 接口的数据结构

    1. Array

    let arr = ['a', 'b', 'c']
    console.log(arr);
    let it = arr[Symbol.iterator]()
    console.log(it.next()); // {value: 'a', done: false}
    

    2. Map

    let map = new Map()
    map.set('name', 'es')
    map.set('age', 5)
    map.set('school', 'imooc')
    let it = map[Symbol.iterator]()
    console.log(it.next()); //{value: ['name', 'es'], done: false}
    

    3. Set

    4. String

    5. TypedArray

    6. 函数的 arguments 对象

    7. NodeList 对象

    6.3 不支持遍历的数据结构“可遍历”

    1. 不可迭代对象

    In order to be iterable, non-array objects must have a Symbol.iterator method

    let courses = {
      allCourse: {
        frontend: ['ES', '小程序', 'Vue', 'React'],
        backend: ['Java', 'Python', 'SpringBoot'],
        webapp: ['Android', 'IOS']
      }
    }
    // Uncaught TypeError: Invalid attempt to iterate non-iterable instance.
    // In order to be iterable, non-array objects must have a [Symbol.iterator]() method.
    for(let c of courses) {
      console.log(c);
    }
    

    2. 实现 next 使不可迭代对象可迭代

    • 可迭代协议:Symbol.iterator

    • 迭代器协议: return { next() { return { value, done } } }

    courses[Symbol.iterator] = function() {
      let allCourse = this.allCourse
      let keys = Reflect.ownKeys(allCourse) // ['frontend', 'backend', 'webapp']
      let values = []
      return {
        next() {
          if(!values.length) {
            if(keys.length) {
              values = allCourse[keys[0]]
              keys.shift()
            }
          }
          return {
            done: !values.length,
            value: values.shift()
          }
        }
      }
    }
    for(let c of courses) {
      console.log(c);
    }
    

    3. 结合 generator 使不可迭代对象可迭代

    courses[Symbol.iterator] = function* () {
      let allCourse = this.allCourse
      let keys = Reflect.ownKeys(allCourse)
      let values = []
      while(1) {
        if(!values.length) {
          if(keys.length) {
            values = allCourse[keys[0]]
            keys.shift()
            yield values.shift()
          } else {
            return false
          }
        } else {
          yield values.shift()
        }
      }
    }
    for(let c of courses) {
      console.log(c);
    }
    

    7 模块化 Module

    7.1 模块化规范

    • CommonJS: Node.js
    • AMD: require.js
    • CMD: sea.js
    • ESModule

    7.2 ES6

    1. export .. + import { .. } from ..

    • 导入导出名字需一致
    1. 导出 export ..
    • 同一文件可 export 多次
    const a = 5
    const b = 'imooc'
    const sum = (x, y) => x + y
    const obj = {
      name: 'es'
    }
    class People {
      constructor(name) {
        this.name = name
      }
      showName() {
        console.log(this.name);
      }
    }
    
    export { a, b, sum, obj, People }
    
    1. 导入 import .. from ..
    import { a, b, sum, obj, People } from './module'
    console.log(a, b);
    console.log(sum(2, 5));
    console.log(obj);
    let p = new People('zouzou')
    p.showName()
    
    • 起别名
    import { a as aa } from './module'
    console.log(aa);
    

    2. export default .. + import .. from ..

    • 导入导出名字可以不一致
    1. 导出 export default ..
    • 每个模块只能存在一次 export default
    const a = 5
    export default a
    
    1. 导入 import .. from ..
    import aa from './module'
    console.log(aa);
    

    3. export .. + export default ..

    1. 导出
    function sum(x, y) {
      return x + y
    }
    export default sum
    
    export const str = 'imooc'
    
    1. 导入
    import add, { str } from './module'
    console.log(add(1, 2));
    console.log(str);
    

    4. export default { .. }

    1. 导出
    const a = 5
    const b = 'imooc'
    const sum = (x, y) => x + y
    const obj = {
      name: 'es'
    }
    class People {
      constructor(name) {
        this.name = name
      }
      showName() {
        console.log(this.name);
      }
    }
    export default {
      a, b, sum, obj, People
    }
    
    1. 导入
    // import mod from './module'
    // console.log(mod); // { .. }
    import * as mod from './module'
    console.log(mod); // { default: { .. }}
    
  • 相关阅读:
    07-selenium、PhantomJS(无头浏览器)
    06爬虫-异步协程
    Numpy数值类型与数值运算-03
    05爬虫-requests模块基础(2)
    初识Matplotlib-01
    03爬虫-requests模块基础(1)
    Django安装与简单事例-02
    JavaWeb学习总结(二):Http协议
    Tomcat学习总结(一):目录简介
    Web服务器学习总结(一):web服务器简介
  • 原文地址:https://www.cnblogs.com/pleaseAnswer/p/16285493.html
Copyright © 2020-2023  润新知