es6-es11系列4 异步编程

js 先执行同步任务,异步任务会先放入event table内再经过event queue排队执行

AJAX原理

先创建一个AJAX对象通过XMLHttpRequest
通过open方法打开一个连接
通过send发送请求
通过onreadyStatechange事件监听服务器的返回数据

Promise

Promise内的代码在new的时候也是同步代码,例如下面的情况会先输入1再输入2

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

Promise的状态不可以逆,例如resole之后就不能再执行reject了
Promise的常用静态方法,无需new操作
Promise.resolve // 应用场景,简单返回一个结果为字符串的Promise对象
Promise.reject // 应用场景,简单返回一个结果为字符串的Promise对象
Promise.all // 应用场景,遍历图片数组,全部上传成功后提示用户完成
promise.race // 应用场景图片加载超时

Generator

Generator函数设计的意思重点是把异步操作放到yeild表达式中,等调用next之后再执行,也是解决回调地狱的一种方案,其实后面es7的async await更好
生成器函数最基本的用法

// 定义个生成器函数,一般会在function后面加上一个星号来表示
let foo = function* () {
    for (let i = 0; i < 3; i++) {
        yield i
    }
}
// 将生成器函数肤质给一个变量,注意此时生成器函数内部的for循环并不会执行,可以在yield i 上面打印个log测试一下
let f = foo()
// 调用f下的next()方法,最后一次因为循环结束,所以没有值被返回
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不能被当做构造函数的方式被使用,换句话说就是不能被new,会报错foo is not a constructor
yield 关键字只能在Generator函数内部使用。例如将上面的for循环改成forEach则会因为作用域的问题报错
一个最典型的应用场景,写一个函数用来生成7得倍数

function* gen(i = 0) {
    while (true) {
        if (i % 7 === 0) {
            yield i
        }
        i++
    }
}
let g = gen()
console.log(g.next()) // 0
console.log(g.next()) // 7
console.log(g.next()) // 14

解决ajax的回调地狱场景,例如有三个接口有依赖关系,常规的写法是嵌套的方式把下一个接口写到回调函数中,使用生成器可以用下面这种写法

function request(url,data = 0) {
    // 使用setTimeout来比喻ajax
    setTimeout(function () { 
        reportData.next(data + 1) // 每次都根据上一次的返回结果去请求下一个接口
    }, 1000)
}

function* gen() {
    let res1 = yield request('api/1')
    console.log(res1) // 1
    let res2 = yield request('api/2',res1)
    console.log(res2) // 2
    let res3 = yield request('api/3',res2)
    console.log(res3) // 3
}

let reportData = gen()
reportData.next() // 开始第一次

Iterator

迭代器是一种接口机制,为不同的数据结构提供统一的访问机制
主要服务与for...of,让步支持遍历的数据结构可遍历
先手写一个迭代器,感觉和Generator非常像

// 手写一个迭代器
function myIterator(arr) {
    let index = 0
    return {
        next() {
            // 这里或者用循环的方式写也可以
            return index < arr.length ? {
                value: arr[index++],
                done: false
            } : {
                value: undefined,
                done: true
            }
        }
    }
}
let it = myIterator ([1,2,3])
console.log(it.next())
console.log(it.next())
console.log(it.next())
console.log(it.next())

一个可迭代的对象,它的属性中必需要有一个Symbol(Symbol.iterator)属性,可以打印一个数组对象然后看看它的Prototype下面属性值Symbol(Symbol.iterator)
例如:

let arr = [1,2,3]
let it = arr[Symbol.iterator]() // 因为数组Prototype下有Symbol.iterator这个迭代器
console.log(it.next())
console.log(it.next())
console.log(it.next())

原生具备Iterator接口的数据结构
Array
Map
Set
String
TypedArray
arguments对象
NodeList对象

可迭代协议: Symbol.iterator

迭代器协议:return { next(){ return{value, done} }
例子:为一个不可迭代的对象添加迭代协议

// 一个不能迭代的对象
let course = {
    allCourse: {
        frontEnd: ['es', 'mp', 'vue'],
        backEnd: ['java', 'python'],
        webapp: ['android', 'ios']
    }
}
// 自己为这个对象实现一个迭代器协议
course[Symbol.iterator] = function* () {
    let allCourse = this.allCourse
    let keys = Reflect.ownKeys(allCourse)
    let values = []
    while (true) {
        if (!values.length) {
            if (keys.length) {
                values = allCourse[keys[0]]
                keys.shift()
                yield values.shift()
            } else {
                return false
            }
        } else {
            yield values.shift()
        }
    }
}
// 可以用for...of遍历了
for(let c of course){
    console.log(c)
}

模块化

CommonJs :基于Node.js
AMD:require.js 依赖前置
CMD::sea.js 就近原则
UMD:集结了 CommonJs、CMD、AMD 的规范于一身
ES6:2015年原生js提供的模块化规范

关键字
export
import
as
export default

本站文章如未注明均为原创 | 文章作者:刘晓帆 | 转载请注明来自:前端印象

发表评论

邮箱地址不会被公开。 必填项已用*标注

浏览量:483 次浏览