④ 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: { .. }}
posted on 2022-05-18 17:17  pleaseAnswer  阅读(27)  评论(0编辑  收藏  举报