1 异步操作必备知识
1.1 异步操作前置知识
1. js 是单线程的
2. 同步任务与异步任务
3. ajax 原理
- 前后端数据分离 前端 <-> 后端 ajax
4. callback hell
- 回调地狱
2 Ajax 原理与 Callback Hell
2.1 ajax 原理
一种前后端的交互方式,可实现页面的部分刷新
-
创建
XMLHttpRequest
对象 -
发送请求
-
服务端响应
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 函数
-
generator
函数不会立即执行,而是生成迭代器对象 -
迭代器函数调用
next
语句,会执行到yeild
语句为止 -
再次调用
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}
注意
-
generator
函数不能作为构造函数 -
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 ..
- 导入导出名字需一致
- 导出
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 }
- 导入
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 ..
- 导入导出名字可以不一致
- 导出
export default ..
- 每个模块只能存在一次
export default
const a = 5
export default a
- 导入
import .. from ..
import aa from './module'
console.log(aa);
3. export .. + export default ..
- 导出
function sum(x, y) {
return x + y
}
export default sum
export const str = 'imooc'
- 导入
import add, { str } from './module'
console.log(add(1, 2));
console.log(str);
4. export default { .. }
- 导出
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
}
- 导入
// import mod from './module'
// console.log(mod); // { .. }
import * as mod from './module'
console.log(mod); // { default: { .. }}