迭代器Iterator
迭代器 是一个对象 也可称为可迭代对象,提供一个next方法 去获取迭代器中下一项,next方法是一个函数返回一个对象{done:boolean,value:any}
function makeIterator(array) {
var nextIndex = 0;
return {
next() {
return nextIndex<array.length?{value: array[nextIndex++],done:false}:{done:true}
}
}
}
var iterator = makeIterator([10,20,30])
console.log(iterator.next()) // {value: 10, done: false}
console.log(iterator.next())// {value: 20, done: false}
console.log(iterator.next())// {value: 30, done: false}
console.log(iterator.next()) // {done: true}
可迭代对象
可迭代对象内部实现了一个@@Iterator方法,就是数据结构或者原型链上有一个Symbol.iterator属性(遍历器生产函数),它返回一个遍历器对象。只要一个数据结构有S ymbol.iterator就可以认为是可迭代的。
const obj = {
a:1
}
for (const i of obj) { // 报错: obj is not iterable
console.log(i)
}
// 手动给obj加Symbol.iterator属性 它就变为可迭代对象
obj[Symbol.iterator] = function() {return makeIterator([1,2,3]) }
// 可以使用 for of 进行访问
for (const i of obj) {
console.log(i) // 1 2 3
}
// 可以被Arraty.form 转为数组
console.log(Array.from(obj)) // [1,2,3]
// for in 是可遍历
console.log(Object.getOwnPropertyDescriptors(obj))
/* {
a: { value: 1, writable: true, enumerable: true, configurable: true },
[Symbol(Symbol.iterator)]: {
value: [Function (anonymous)],
writable: true,
enumerable: true,
configurable: true
}
} */
for (const i in obj) {
console.log(i) // a
}
Object.defineProperty(obj,'b', { value: 2, writable: true, enumerable: true, configurable: true })
for (const i in obj) {
console.log('test: '+i) // a b
}
Object.defineProperty(obj,'b', { value: 2, writable: true, enumerable: false, configurable: true })
for (const i in obj) {
console.log(i) // a
}
*特性:
- 具Symbol.iterator属性
- 可以使用 for of 进行访问
- 可以被Arraty.form 转为数组
这里有个常见误区,我们老说的可以for in遍历其实是枚举,就是数据属性的enumrable 为true 可以被for in 访问元素
而 可迭代(可遍历),是有Symbol.iterator 属性,可以被 for of 访问原型
对象可以用for in 不能 用for of ,其实就是因为这个,可以说对象可以枚举,但是不可迭代
原生的可迭代对象
- 数组
- SET
- MAP
- 类数组 arguments DOM NODELIST
- GENERATOR对象
- 字符串
手动实现迭代器
就是调用数据的Symbol.iterator属性 获得迭代器构造函数 调用next
function Forof(data,cb) {
if(typeof data[Symbol.iterator] !== "function") {
throw new TypeError(' is not iterable');
}
if(typeof cb !== "function") {
throw new TypeError('cb is not function');
}
const iterator = data[Symbol.iterator]();
let result = iterator.next();
while(!result.done) {
cb(result.value);
result = iterator.next();
}
}
Forof([1,2],console.log) // 1,2
内置迭代器
- Object.keys
- Object.values
- Object.entries
异步promise
回调地狱
Promise 是为了解决回调地狱的问题,回调地狱是一个请求成功后再执行另一个请求再执行 如此嵌套 导致代码看起来比较复杂,不易维护
定义
Promise 是一个对象 里面保存着未来才会结束的事件,比如异步操作。
特点:
- 3种状态 pending rejected fulfilled 只由异步操作决定当时是那种状态,其他操作都不能改变这个状态,不受外界影响
- 状态一旦改变,就不会再变。一开始是pending, 只能从pending 变为fulfilled 或者 rejected ,没有其他情况。
APi
- 创建实例 new Promise 成功resolve 失败reject. 调用这个实力的then方法,传递两个参数分别是 成功和失败的回调函数
const promise = new Promise((reslove,reject) {
if(/* 异步成功*/) {
resolve(value)
} else {
reject(error)
}
})
promise.then(funtion(value) {
// success
},function(error){
// failed
}).catch(error => {
// 发生错误的回调
})
.finally(() => {
// 不管最后啥状态 一定会执行
});
- p = Promise.all(a1,a2,a3…) 将多个p promise 包装成一个 参数是p romise实例或者可迭代对象。只有当a1,a2,a3 全部变为fulfilled 整体p才会fulfilled;只要其中一个rejected p就会rejected,会回传第一个rejected的值给回调函数
- p=Promise.race(a1,a2,a2…) 同上也是包装多个promise, 只要其中一个率先改变状态,就会把这个返回值传递给回调
- p = Promise.allSettled(a1,a2,a3…)同上也是包装多个promise, 当所有的实例的状态都确定时,会把所有的值都传递给回调函数
- p = Promise.resolve(…)将现有对象转为p romose对象,如果参数时promise 就原样返回,如果是可迭代对象(具有t hen方法的对象)会把这个对象转为promise 并且立即调用then方法;如果参数时原始值,就返回一个新的promise 状态时fulfilled;不带参数,返回fulfilled状态的promise对象
- p = Promise.reject() 返回一个新的 Promise 实例,该实例的状态为
rejected
。
实例
-
红绿灯:3s red 3s green 1s yellow
// 回调地狱写法 const stepCB = () => { setTimeout(()=> { console.log('red'); setTimeout(()=> { console.log('green') setTimeout(()=> { console.log('yellow') stepCB(); },1000) },2000) },3000) } // promise写法 const promiseGRBL = (time,color) => { return new Promise((reslove,reject)=> { setTimeout(()=> { console.log(color); resolve() },time) }) } const step = () => { promiseRGBL(3000,'red').then(()=> { return promiseGRBL(2000,'green'); }).then(()=> { return promiseGRBL(1000,'yellow'); }).then(()=> { step(); }) }
-
把xmlHttpRequest 封装成同步写法
function request(config) { const {method,url,resposeType,data} = config; return new Promise((resolve,reject) => { const xhr = new XmlHttpRequest(); xhr.open(method,url); xhr.onreadystatechange = function() { if(this.readyState !== 4) { return } if(this.status === 200) { resolve(this.response) } else { reject(new Error(this.stateText)) } } xhr.responsType = responseType; xhr.setRequsetHeader('Accept',"application/json"); data ? xhr.send(data) : xhr.send() }) } requst({method:'get',url:'xxx',responseTyepe:'json',data:{id:1}})
手写 promise
- 用构造函数 创建一个对象 对象具有state 状态默认值是pending, result 存储成功结果,reason存储失败原因,onResolves 存在异步成功回调,onRejects 存储异步失败回调,构造函数有一个promise构造器函数的参数,在构造函数里面调用构造器,传入两个参数,一个resolve函数, 一个reject函数。在构造函数内部创建一个resolve函数判断状态是pending时就把状态改为fuilfilled,把resolve的结果赋值给result,同上遍历执行onResolves里的的函数;同理reject函数也是如此,只是存的失败的reason 状态是rejected 遍历执行的是onRejects函数。
- 构造函数的原型链上定义一个then方法,传入两个参数,一个成功回调函数onResolve,一个失败的回调函数onReject,判断这两个类型,如果不是函数就赋值为参数是value 返回值是value的函数,。为了能链式调用,then方法返回一个新的promise对象。在这个对象里头,判断state是pending的时候表示是异步,就把回调函数存起来。state是fulfilled的时候 就执行onResolve函数,并把结果传给一个resolvePromise函数,同理当state是rejected的时候也一样,就是执行onReject函数。
- resolvePromise函数 传入3个参数 回调的返回值 resolve reject函数,判断回调返回值的类型是不是对象或者函数,如果是,就判断它的then类型,如果是函数就运行这个函数,把回调值传入当this reolve reject 传入当参数,如果不是 就resolve这个值
function MyPromise(fn) {
this.state = 'pending';
this.result = null;
this.reason = null;
this.onResolves = [];
this.onRejects = [];
const resolve = (result) => {
if(this.state === 'pending') {
this.state = 'fulfilled'
this.result = result;
this.onResolves.forEach(function(fn) {fn()})
}
}
const reject = (reason) => {
if(this.state === 'pending') {
this.state = 'rejected'
this.reason = reason;
this.onRejects.forEach(function(fn) {fn()})
}
}
try { fn(resolve,reject)} catch(e) {reject(e)}
}
const promoseResolve = (value,resolve,reject) => {
if(typeof value !== null &&(typeof value === 'function' || typeof value === 'object')) {
const then = x.then;
if(typeof then === 'function') {
try{
then.call(value,resolve,reject)
} catch(e) {
reject(e)
}
} else {
resolve(value)
}
} else {
resolve(value)
}
}
MyPromise.prototype.then = function(onResolve,onReject) {
// 根据规范,如果then的参数不是function,则我们需要忽略它, 让链式调用继续往下执行
if(typeof onResolve !=== 'function') {
onResolve = (result)=> result
}
if(typeof onReject !=== 'function') {
onReject = (reason)=> reason
}
const promise = new MyPromise((resolve,reject)=>{
const onRejectWrap = ()=> {
// 这里加setTimeout 是因为promise并不是一成功就执行回调 而是放在任务队列中,这里是放在宏任务中
setTimeout(()=> {
const reason = onReject();
promoseResolve(reason,resolve,reject)
},0)
}
const onResolveWrap = ()=> {
setTimeout(()=> {
const result = onResolve()
promoseResolve(result,resolve,reject)
},0)
}
if(this.state === 'pending') {
this.onResolves.push(onResolveWrap);
this.onRejects.push(onRejectWrap);
}
if(this.state === 'fulfilled') {
onResolveWrap()
}
if(this.state === 'onReject') {
onRejectWrap()
}
})
return promise;
}
// 失败执行回调
MyPromise.prototype.catch = function(fn) {
this.then(null,fn)
}
// MyPromise.resolve执行回调,并在then中return结果传递给后面的Promise
PromiseA.prototype.finally = function (fn) {
this.then(value => PromiseA.resolve(fn()).then(() => value),
reason => PromiseA.resolve(fn()).then(() => {
throw reason
})
)
}
// 如果类型是primise就直接返回 如果不是就包装成一个resolve promise
MyPromise.resolve = function(data) {
if(data instanceOf MyPromise) {
return data
}
return new MyPromise(resolve => {
resolve(data)
})
}
// 包装成一个reject的promise
MyPromise.reject = function(data) {
return new MyPromise((_,reject) => {
reject(data)
})
}
// 返回一个promise 遍历所有的传入的数组 如果是promise就直接then 不是就是resolve包装成promise 再 then, then成功回调里头 计数 如果都成功就resolve 成功返回数组,如果不成功 就直接reject reason
MyPromise.all = function(list) {
return new MyPromise((resolve,reject) => {
let count = 0;
const len = list.length;
const result = []
for(let i =0;i<len;i++) {
MyPromise.resolve(list[i]).then(v=> {
result[i] = v
if(++count === len ) {
resolve(result)
}
},r=> {
reject(r)
})
}
})
}
// 返回一个promise 遍历传入的数组 如果是promise直接then 如果不是就先resolve包装为promise再then, then里面,如果成功就直接成功 返回结果,失败就直接失败返回原因
MyPromise.race = function(list) {
return new MyPromise((resolve,reject) => {
const len = list.length;
for(let i =0;i<len;i++) {
MyPromise.resolve(list[i]).then(v=> {
resolve(v)
},r=> {
resolve(r)
})
}
})
// 返回一个promise 遍历传入的数组 如果是promise直接then 如果不是就先resolve包装为promise再then, then里面,成功就把成功及结果存起来 装饰fulfilled 失败就把失败原因存起来,状态是rejected 。当所有数据都执行完时 就 resolve 所有结果
MyPromise.allSetteld = function(list) {
return new MyPromise((resolve,reject) => {
let count = 0;
const len = list.length;
const result = []
for(let i =0;i<len;i++) {
MyPromise.resolve(list[i]).then(v=> {
result.push({state:'fulfilled',value:v})
if(++count === len ) {
resolve(result)
}
},r=> {
result.push({state:'rejected',reason:r})
if(++count === len ) {
resolve(result)
}
})
}
})
}
异步generator 迭代器生成器
Generator 是一个普通函数,function 和函数名直接有个*号,函数里面用关键字yield 定义内部状态; 调用这个函数不返回结果,返回的是迭代器对象,有一个then方法,返回{value:值,done: true or false },then 方法 调用遇到yield 暂停 返回yield定义的状态,再then 继续执行,直到执行完,done变为false
function* generator() {
yield 1;
yield 2;
yield 3
}
const gen = generator()
gen.then() // {value:1,done:false}
gen.then()// {value:2,done:false}
gen.then()// {value:3,done:false}
gen.then()// {value:undefined,done:true}
console.log(g[Symbol.iterator]() === g) // true generator 调用返回的是迭代器对象 它的Symbo.iteratro 属性 也是一个遍历器生成函数 调用返回自己
// 用递归遍历
const next = () => {
let data = g1.next()
console.log(data)
if(data.done) {
return
}
next()
}
next()
generator生成器实现方式:核心是有个context去保存上下文,前一个后一个值,以及done状态,每次执行时都会判断从哪里开始执行。
function gen$(_context) {
while (true) {
switch (_context.pre = _context.next) {
case 0:
_context.next = 2;
return 'result1';
case 2:
_context.next = 4;
return 'result2';
case 4:
_context.next = 6;
return 'result2';
case 6:
case 'end':
return context.stop()
}
}
}
const context = {
pre :0,
next:0,
done: false,
stop:function (){
this.done = true
}
}
let gen = function () {
return {
next: function () {
return {value: context.done ? undefined: gen$(context),done: context.done}
}
}
}
var g = gen();
console.log(g.next()) // { value: 'result1', done: false }
console.log(g.next()) // { value: 'result2', done: false }
console.log(g.next()) // { value: 'result2', done: false }
console.log(g.next()) // { value: undefined, done: true }
异步async await
-
Async await 是 genetator的语法糖
-
执行async函数 返回的一定是promise对象
-
await
能够返回Promise的resolve/reject的值 相当于then
Async await的实现:就是基于generator的封装,传入genarator 执行遍历器,返回promise ;promise 里面执行 递归调用next,这里为了兼容primise和普通值 用promise resolve包装统一为promise, 当遍历结束时 resolve value值
function run(gen) {
return new Promise((resolve,reject) => {
const g= gen();
function next(value) {
let result = null
try {
result = g.next(value);
} catch (e) {
return reject(e)
}
if(result.done) {
return resolve(result.value)
}
Promise.resolve(result.value).then(v=> next(v),err=> g.throw(err))
}
next()
})
}
function* genTest (){
const r1 = yield Promise.resolve(1);
const r2 = yield Promise.resolve(r1+1);
const r3 = yield new Promise(resolve=> setTimeout(()=> resolve(r2+1),1000));
const r4 = yield r3
return r4
}
// 调用
run(genTest).then(v=> {
console.log(v) // 3
})