网站开发毕业设计文献综述网站开发的两种模式
news/
2025/9/27 3:54:09/
文章来源:
网站开发毕业设计文献综述,网站开发的两种模式,it运维系统详细设计,做苗木网站哪家做得好Python微信订餐小程序课程视频
https://edu.csdn.net/course/detail/36074
Python实战量化交易理财系统
https://edu.csdn.net/course/detail/35475
掌握JavaScript中的迭代器和生成器#xff0c;顺便了解一下async、await的原理
前言
相信很多人对迭代器和生成器都不陌…Python微信订餐小程序课程视频
https://edu.csdn.net/course/detail/36074
Python实战量化交易理财系统
https://edu.csdn.net/course/detail/35475
掌握JavaScript中的迭代器和生成器顺便了解一下async、await的原理
前言
相信很多人对迭代器和生成器都不陌生当提到async和await的原理时大部分人可能都知道async、await是Promise生成器的语法糖其原理具体是怎么做的呢下面通过这篇文章带你详细了解一下迭代器和生成器以及带你从生成器一步步推导到async和await。
1.迭代器Iterator
1.1.什么是迭代器 迭代器是确使用户在容器对象链表或数组上遍访的对象使用该接口无需关心对象的内部实现细节。 迭代器的定义可能比较抽象简单来说迭代器就是一个对象可用于帮助我们对某个数据结构链表、数组进行遍历 在JavaScript中迭代器也是一个具体的对象并且这个对象必须符合迭代器协议iterator protocol 什么是迭代器协议 在JavaScript中就是指这个对象必须实现一个特定的next方法并且next方法有如下要求next方法可接收0个或者1个参数在生成器中next可以接收1个参数并且需返回一个对象对象包含以下两个属性 done值为Boolean如果迭代器可以迭代产生下一个值就为false如果已经迭代完毕就为truevalue迭代器返回的值如果done为falsevalue一般为undefined 编写一个最简单的迭代器
const iterator {next: function() {return { done: false, value: 123 }}
}
1.2.迭代器的基本使用 明白了迭代器的基本定义下面就来实现一下符合迭代器协议的对象吧并且看看其它的一些基本用法。比如需要通过迭代器访问一个数组 1创建一个迭代器对象
const names [curry, kobe, klay]let index 0 // 通过一个index来记录当前访问的位置
const iterator {next() {if (index names.length) {return { done: false, value: names[index] }} else {return { done: true, value: undefined }}}
}console.log(iterator.next()) // { done: false, value: curry }
console.log(iterator.next()) // { done: false, value: kobe }
console.log(iterator.next()) // { done: false, value: klay }
console.log(iterator.next()) // { done: true, value: undefined }
2实现生成迭代器的函数
如果每次需要去访问一个数组就去编写一个对应的迭代器对象肯定是很麻烦的可以封装一个函数用于生成一个访问数组的迭代器
function createIterator(arr) {let index 0return {next() {if (index arr.length) {return { done: false, value: arr[index] }} else {return { done: true, value: undefined }}}}
}
const names [curry, kobe, klay]
// 调用createIterator函数生成一个访问names数组的迭代器
const namesIterator createIterator(names)console.log(namesIterator.next()) // { done: false, value: curry }
console.log(namesIterator.next()) // { done: false, value: kobe }
console.log(namesIterator.next()) // { done: false, value: klay }
console.log(namesIterator.next()) // { done: true, value: undefined }
1.3.可迭代对象
1.3.1.什么是可迭代对象 上面提到了迭代器是一个对象并且符合迭代器协议那么什么是可迭代对象呢它与迭代器又有什么区别 迭代器是一个符合**迭代器协议iterator protocol**的对象对象内实现了一个特定的next方法而可迭代对象是一个符合**可迭代协议iterable protocol**的对象对象内实现了一个Symbol.iterator方法并且该方法返回一个迭代器对象所以可以说可迭代对象包含了迭代器对象可迭代对象中实现了一个特定方法用于返回迭代器对象
如下iteratorObj就是一个可迭代对象
const iteratorObj {names: [curry, kobe, klay],[Symbol.iterator]: function() {let index 0return {// 注意这里的next需要使用箭头函数否则this访问不到iteratorObjnext: () {if (index this.names.length) {return { done: false, value: this.names[index] }} else {return { done: true, value: undefined }}}}}
}
// 调用iteratorObj中的Symbol.iterator得到一个迭代器
const iterator iteratorObj[Symbol.iterator]()console.log(iterator.next()) // { done: false, value: curry }
console.log(iterator.next()) // { done: false, value: kobe }
console.log(iterator.next()) // { done: false, value: klay }
console.log(iterator.next()) // { done: true, value: undefined }
1.3.2.JS内置的可迭代对象 上面的可迭代对象都是由自己实现的其实在JavaScript中为我们提供了很多可迭代对象如String、Array、Map、Set、arguments对象、NodeListDOM集合等。 // 1.String
const str abcconst strIterator str[Symbol.iterator]()
console.log(strIterator.next()) // { value: a, done: false }
console.log(strIterator.next()) // { value: b, done: false }
console.log(strIterator.next()) // { value: c, done: false }
console.log(strIterator.next()) // { value: undefined, done: true }// 2.Array
const names [curry, kobe, klay]
console.log(names[Symbol.iterator])const namesIterator names[Symbol.iterator]()
console.log(namesIterator.next()) // { value: curry, done: false }
console.log(namesIterator.next()) // { value: kobe, done: false }
console.log(namesIterator.next()) // { value: klay, done: false }
console.log(namesIterator.next()) // { value: undefined, done: true }// 3.Map/Set
const set new Set
set.add(10)
set.add(20)
set.add(30)const setIterator set[Symbol.iterator]()
console.log(setIterator.next()) // { value: 10, done: false }
console.log(setIterator.next()) // { value: 20, done: false }
console.log(setIterator.next()) // { value: 30, done: false }
console.log(setIterator.next()) // { value: undefined, done: true }
1.3.3.可迭代对象应用场景 可迭代对象在实际应用中特别常见像一些语法的使用、创建一些对象和方法调用都用到了可迭代对象。 JS中的语法for…of、展开语法、解构等。 for…of可用于遍历一个可迭代对象其原理就是利用迭代器的next函数如果done为false就从返回的对象中拿到value返回给我们而对象不是一个可迭代对象所以对象不能使用for…of遍历 const num [1, 2, 3]
for (const item of num) {console.log(item) // 1 2 3
}
// 遍历上面自己定义的可迭代对象iteratorObj也是可以的
for (const item of iteratorObj) {console.log(item) // curry kobe klay
} const obj { name: curry, name: 30 }
for (const key of obj) {console.log(key)
} 为什么数组能使用展开语法其原理也是用到了迭代器在使用...对数组进行展开时也是通过迭代器的next去获取数组的每一项值然后存放到新数组中 const names [james, green]
// 将数组和iteratorObj通过扩展进行合并
const newNames [...names, ...iteratorObj]
console.log(newNames) // [ james, green, curry, kobe, klay ] 可迭代对象都是可以使用解构语法的像数组、字符串为什么可以使用解构其原因就在这原理也是通过迭代器一个个取值然后再赋值给对应变量 const str abc
const nums [1, 2, 3]const [str1, str2, str3] str
console.log(str1, str2, str3) // a b cconst [num1, num2, num3] nums
console.log(num1, num2, num3) // 1 2 3const [name1, name2, name3] iteratorObj
console.log(name1, name2, name3) // curry kobe klay 注意在扩展语法和解构语法中我们知道数组可以使用但是对象也可以使用呀为什么没有提到对象呢因为对象的扩展和解构是在ES9中新增的特性其原理并不是使用迭代器实现的只是ECMA提供给我们的一种操作对象的新语法而已 JS创建对象new Map([Iterable])、new WeakMap([Iterable])、new Set([Iterable])、new WeakSet([Iterable])等。
// 1.Set
const set new Set(iteratorObj)
console.log(set) // Set(3) { curry, kobe, klay }// 2.Array.from
const names Array.from(iteratorObj)
console.log(names) // [ curry, kobe, klay ]
JS方法调用Promise.all(Iterable)、Promise.race(Iterable)、Array.from(Iterable)等。
// 传入的可迭代对象中的每个值会使用Promise.resolve进行包裹
Promise.all(iteratorObj).then(res {console.log(res) // [ curry, kobe, klay ]
})
扩展现在我们都知道了for…of可用于遍历一个可迭代对象如果在遍历过程中终端了呢因为使用break、continue、return、throw都是可以中断遍历的既然for…of遍历的原理是基于迭代器的那么在for…of中进行中断操作一定是可以被迭代器监听到的上面说了在迭代器中有一个next方法其实还可以指定一个return方法如果遍历过程中断了就会去调用return方法注意return方法也要返回和next方法一样的对象。这种情况就称之为迭代器的中断。
const iteratorObj {names: [curry, kobe, klay],[Symbol.iterator]: function() {let index 0return {next: () {if (index this.names.length) {return { done: false, value: this.names[index] }} else {return { done: true, value: undefined }}},return() {console.log(哎呀我被中断了)return { done: true, value: undefined }}}}
}for (const item of iteratorObj) {console.log(item)if (item kobe) break
} 1.3.4.自定义可迭代类 上面提到了对象不是一个可迭代对象所以对象不能使用for…of遍历如果我们想要实现通过for…of遍历对象呢那么可以自己实现一个类这个类的实例化对象是可迭代对象。 实现一个Person类并且Person类中实现了Symbol.iterator方法用于返回一个迭代器Person类的实例化对象p中包含一个friends数组通过for…of遍历p对象时可以将friends数组的每一项遍历出来
class Person {constructor(name, age, friends) {this.name namethis.age agethis.friends friends}[Symbol.iterator]() {let index 0return {next: () {if (index this.friends.length) {return { done: false, value: this.friends[index] }} else {return { done: true, value: undefined }}}}}
}
简单看一下效果
const p new Person(curry, 30, [kobe, klay, green])
for (const name of p) {console.log(name) // kobe klay green
}
2.生成器Generator
2.1.什么是生成器 生成器是ES6中新增的一种控制函数执行的方案它可以帮助我们控制函数的暂停和执行。生成器是一种特殊的迭代器所以生成器也是一个对象并且可以调用next方法。那么怎么创建一个生成器对象呢 创建生成器对象需要使用生成器函数生成器函数和普通函数不一样主要有以下特点
生成器函数的声明需要在function后加上一个符号*在生成器函数中可以使用yield关键字来分割函数体代码控制函数的执行生成器函数调用的返回值就是生成器对象了
2.2.生成器的基本使用 实现一个生成器函数该函数的执行可以通过返回的生成器对象进行控制。 function* generatorFn() {console.log(函数开始执行~)console.log(函数第一段代码执行...)yieldconsole.log(函数第二段代码执行...)yieldconsole.log(函数第三段代码执行...)yieldconsole.log(函数第四段代码执行...)console.log(函数执行结束~)
}// 调用generatorFn获取生成器
const generator generatorFn()generator.next()
console.log(------------------------)
generator.next()
console.log(------------------------)
generator.next()
console.log(------------------------)
generator.next() 2.3.生成器next方法的返回值 上面说到了生成器是一种特殊的迭代器那么调用next方法肯定也是有返回值的并且返回值是一个包含done、value属性的对象。 function* generatorFn() {console.log(函数第一段代码执行...)yieldconsole.log(函数第二段代码执行...)yieldconsole.log(函数第三段代码执行...)yieldconsole.log(函数第四段代码执行...)
}// 调用generatorFn获取生成器
const generator generatorFn()console.log(generator.next())
console.log(------------------------)
console.log(generator.next())
console.log(------------------------)
console.log(generator.next())
console.log(------------------------)
console.log(generator.next())
从打印结果可以看出来next返回的对象中value是没有值的当执行到最后一段代码后done的值就为true了 如果需要指定next返回值中的value那么可以通过在yield后面跟上一个值或者表达式就可以将对应的值传递到next返回对象value中了。
function* generatorFn() {console.log(函数第一段代码执行...)yield 10console.log(函数第二段代码执行...)yield 20console.log(函数第三段代码执行...)yield 30console.log(函数第四段代码执行...)
}// 调用generatorFn获取生成器
const generator generatorFn()console.log(generator.next())
console.log(------------------------)
console.log(generator.next())
console.log(------------------------)
console.log(generator.next())
console.log(------------------------)
console.log(generator.next()) 观察以上打印结果在执行完第四段代码后调用的next返回值为{ value: undefined, done: true }原因是后面已经没有yield了而且当函数没有指定返回值时最后会默认执行return undefined。
2.4.生成器next方法的参数传递 在前面介绍迭代器定义时提到迭代器的next可以传递0个或1个参数而可以传递1个参数的情况就是生成器的next可以传递一个参数而给每一段代码传递过去的参数可以通过yield来接收。 function* generatorFn(value) {console.log(函数第一段代码执行..., value)const value1 yield 10console.log(函数第二段代码执行..., value1)const value2 yield 20console.log(函数第三段代码执行..., value2)const value3 yield 30console.log(函数第四段代码执行..., value3)
}// 调用generatorFn获取生成器
const generator generatorFn(参数0)console.log(generator.next())
console.log(------------------------)
console.log(generator.next(参数1))
console.log(------------------------)
console.log(generator.next(参数2))
console.log(------------------------)
console.log(generator.next(参数3)) next参数传递解释
next中传递的参数是会被上一个yield接收的这样可以方便下面代码使用这个参数所以给next传递参数需要从第二个next开始传递如果第一段代码需要使用参数呢可以在调用生成器函数时传递参数过去供第一段代码使用 2.5.生成器的return方法 return方法也可以给生成器函数传递参数但是调用return后生成器函数就会中断之后再调用next就不会再继续生成值了。 function* generatorFn() {console.log(函数第一段代码执行...)yieldconsole.log(函数第二段代码执行...)yieldconsole.log(函数第三段代码执行...)yieldconsole.log(函数第四段代码执行...)
}// 调用generatorFn获取生成器
const generator generatorFn()console.log(generator.next())
console.log(------------------------)
console.log(generator.next())
console.log(------------------------)
console.log(generator.return(123))
console.log(------------------------)
console.log(generator.next())
console.log(generator.next())
console.log(generator.next())
console.log(generator.next())
console.log(generator.next()) 上面的执行return方法相当于函数内部执行了return
function* generatorFn() {console.log(函数第一段代码执行...)yieldconsole.log(函数第二段代码执行...)const value yieldreturn valueconsole.log(函数第三段代码执行...)yieldconsole.log(函数第四段代码执行...)
}// 调用generatorFn获取生成器
const generator generatorFn()console.log(generator.next())
console.log(------------------------)
console.log(generator.next())
console.log(------------------------)
console.log(generator.next(123))
console.log(------------------------)
console.log(generator.next())
console.log(generator.next())
console.log(generator.next())
console.log(generator.next())
console.log(generator.next()) 2.6.生成器的throw方法 throw方法可以给生成器函数内部抛出异常。 生成器调用throw方法抛出异常后可以在生成器函数中进行捕获通过try catch捕获异常后后续的代码还是可以正常执行的
function* generatorFn() {console.log(函数第一段代码执行...)yield 10console.log(函数第二段代码执行...)try {yield 20} catch (err) {console.log(err)}console.log(函数第三段代码执行...)yield 30console.log(函数第四段代码执行...)
}// 调用generatorFn获取生成器
const generator generatorFn()console.log(generator.next())
console.log(------------------------)
console.log(generator.next())
console.log(------------------------)
console.log(generator.throw(err message))
console.log(------------------------)
console.log(generator.next()) 2.7.生成器替换迭代器 在前面实现了一个生成迭代器的函数实现过程还需要进行判断并自己返回对应的对象下面就用生成器来实现一个生成迭代器的函数。 方式一根据数组元素的个数执行yield
function* createIterator(arr) {let index 0yield arr[index]yield arr[index]yield arr[index]
}
方式二遍历数组执行yield
function* createIterator(arr) {for (const item of arr) {yield item}
}
方式三执行yield*后面可以跟上一个可迭代对象它会依次迭代其中每一个值
function* createIterator(arr) {yield* arr
}
测试一下以上三种方法执行结果都是一样的
const names [curry, kobe, klay]
const iterator createIterator(names)console.log(iterator.next()) // { value: curry, done: false }
console.log(iterator.next()) // { value: kobe, done: false }
console.log(iterator.next()) // { value: klay, done: false }
console.log(iterator.next()) // { value: undefined, done: true }
3.异步请求的处理方案 在进行异步请求时如果出现了这样一个需求下一次的请求需要拿到上一次请求的结果。如下是使用Promise封装的一个request方法。 function request(url) {return new Promise((resolve, reject) {setTimeout(() {resolve(url)}, 300)})
}
实现上面的需求可以怎么做呢
3.1.方案一使用Promise的then进行嵌套调用
在then中拿到结果后再去做下一次请求以此类推缺点形成了回调地狱
request(/aaa).then(res {request(res /bbb).then(res {request(res /ccc).then(res {console.log(res) // /aaa/bbb/ccc})})
})
3.2.方案二使用Promise的then的返回值
虽然可以解决回调地狱问题但是阅读性不佳
request(/aaa).then(res {return request(res /bbb)
}).then(res {return request(res /ccc)
}).then(res {console.log(res) // /aaa/bbb/ccc
})
3.3.方案三使用Promise和Generator处理
function* getRequestData() {const res1 yield request(/aaa)const res2 yield request(res1 /bbb)const res3 yield request(res2 /ccc)console.log(res3)
}
手动执行生成器的next方法
const generator getRequestData()
generator.next().value.then(res {generator.next(res).value.then(res {generator.next(res).value.then(res {generator.next(res) // /aaa/bbb/ccc})})
}) 自动执行生成器的next方法如果手动执行嵌套层级过多的话是不方便的那么可以借助递归的思想实现一个自动执行生成器的函数
function autoGenerator(generatorFn) {const generator generatorFn()function recursion(res) {const result generator.next(res)// 如果done值为true说明结束了if (result.done) return result.value// 没有结束继续调用Promise的thenresult.value.then(res {recursion(res)})}recursion()
}autoGenerator(getRequestData) // /aaa/bbb/ccc
使用第三方库来执行生成器像自动执行生成器函数早就已经有第三方库帮助我们实现了如co
const co require(co)
co(getRequestData) // /aaa/bbb/ccc
3.4.方案四使用async和await async和await是我们解决异步回调的最终解决方案它可以让我们异步的代码看上去是同步执行的。 async function getRequestData() {const res1 await request(/aaa)const res2 await request(res1 /bbb)const res3 await request(res2 /ccc)console.log(res3)
}getRequestData() // /aaa/bbb/ccc
4.async和await的原理 相信从上面讲述的四个异步请求的处理方案中就可以看出来async、await和生成器的关系了。 将生成器函数的*换成async将生成器函数中的yield换成await两种方案所体现出的效果和代码书写形式几乎差不多 总结
async和await的原理其实就是Promise生成器实现的为什么async、await能够让异步代码看上去是同步执行的其原因就在于生成器的next方法可以对函数内代码的执行进行控制当上一次请求拿到结果后再去执行下一次next所以为什么说async和await只是Promise生成器的语法糖其原理就在这
__EOF__
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m3NUTKRy-1649092542787)(https://blog.csdn.net/MomentYY/p/16100015.html)]MomentYY 本文链接https://blog.csdn.net/MomentYY/p/16100015.html关于博主评论和私信会在第一时间回复。或者直接私信我。版权声明本博客所有文章除特别声明外均采用 BY-NC-SA 许可协议。转载请注明出处声援博主如果您觉得文章对您有帮助可以点击文章右下角**【[推荐](javascript:void(0)】**一下。您的鼓励是博主的最大动力
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/919060.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!