受
javascript语言特性的影响,编程过程中充斥着大量异步回调,这会让代码维护起来特别麻烦,一步步走向回调地狱。社区中最早提出Promise解决方案,es6将其融入语法标准,并提供了generator、async,向类同步编程不断努力。本文会通过这三个方面演示类同步进化过程。
Promise提供异步编程的容器,包含异步代码,在得到异步结果时,通过resolve传递数据(resove对应then所指定的函数,其实也就是单个过程的异步回调,可以理解成将之前的回调函数放在then方法中定义)。
function ajax(url, success) {
var xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.status == 200 && xhr.readyState == 4) {
//将请求结果作为实参传入成功回调
success(xhr.responseText);
}
}
}
//如果两个异步过程有先后顺序,则会出现这种嵌套情况
ajax("http://vebcoder.cn:9527/api/getTypeOne", function (res) {
console.log(res);
ajax("http://vebcoder.cn:9527/api/goodList", function (res) {
console.log(res);
})
})
function ajax(url) {
//promise容器包裹异步过程
return new Promise(resolve => {
var xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.status == 200 && xhr.readyState == 4) {
//将异步结果使用resolve进行传递
resolve(xhr.responseText);
}
}
})
}
ajax("http://vebcoder.cn:9527/api/getTypeOne")
.then(res=>{
console.log(JSON.parse(res))
})
.then(()=>{
return ajax("http://vebcoder.cn:9527/api/goodList")
})
.then(res=>{
console.log(JSON.parse(res))
})
解决了回调函数横向发展的问题,变成了纵向发展结构。直观确实很直观但是一大堆的then方法,!接下来generator登场
乍一看,generator不过是一个有多个返回值的函数而已,奥妙在于如果不调用next方法,代码会停止执行的。
//function后加*
function* Gen(){
console.log(1);
yield;
console.log(2)
yield;
console.log(3);
return;
}
//调用函数获得指针对象
var g=Gen();
g.next();//1
g.next();//2
g.next();//3
第一次调用next执行到第一个yield,第三次执行到函数末尾return的位置。
function* Gen(){
//用变量接收yield命令
var res=yield;
console.log(res)
}
var g=Gen();
g.next();
//next传入参数
g.next(100);//100
第一次调用next方法,代码执行到yield停止执行。第二次调用next传入参数,代码继续执行,并将传入next的参数赋值给res变量。next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值。
那么我们就可以这样做:
//上述封装的ajax方法-传统形式
function* Gen(){
//请求成功开始下一步
ajax("http://vebcoder.cn:9527/api/getTypeOne",res=>{g.next(res)})
// 接收yield返回值
let res=yield;
console.log(res)//请求的数据结果
}
var g=Gen();
// 开始执行代码
g.next();
//上述封装的ajax方法-Promise形式
function* Gen(){
//请求成功开始下一步
ajax("http://vebcoder.cn:9527/api/getTypeOne").then(res=>{g.next(res)})
// 接收yield返回值
let res=yield;
console.log(res)//请求的数据结果
}
var g=Gen();
// 开始执行代码
g.next();
使用口诀:上一步回调,下一步,接收yield等待结果传入
按理说这种形式已经不错了,不用再往下看了,除非你能忍住!
async是generator的语法糖,你只需关注两部、步操作就行。相当于对Promise和generator进行了融合。
async function Asy(){
//等待promise实例
let res=await ajax("http://vebcoder.cn:9527/api/getTypeOne")
console.log(res)//请求的数据结果
}
Asy();
//结合自执行函数
;(async function(){
let res=await ajax("http://vebcoder.cn:9527/api/getTypeOne")
console.log(res)//请求的数据结果
}())
async函数就是将Generator 函数的星号(*)替换成async,将yield替换成await。
注意:await后面需要跟一个promise实例,无需手动传递结果!
最后来一个对比(有次序的异步过程):
//传统方式(对应上述传统ajax封装形式)
ajax("http://vebcoder.cn:9527/api/getTypeOne", function (res) {
console.log(res);
ajax("http://vebcoder.cn:9527/api/goodList", function (res) {
console.log(res);
})
})
//promise (对应上述promise ajax封装形式)
ajax("http://vebcoder.cn:9527/api/getTypeOne")
.then(res=>{
console.log(JSON.parse(res))
})
.then(()=>{
return ajax("http://vebcoder.cn:9527/api/goodList")
})
.then(res=>{
console.log(JSON.parse(res))
})
//generator (对应上述promise ajax封装形式)
function* Gen(){
//请求成功开始下一步
ajax("http://vebcoder.cn:9527/api/getTypeOne").then(res=>{g.next(res)})
// 接收yield返回值
let res=yield;
console.log(res);
ajax("http://vebcoder.cn:9527/api/goodList").then(res=>{g.next(res)})
// 接收yield返回值
let res2=yield;
console.log(res2);
}
var g=Gen();
// 开始执行代码
g.next();
//async (对应上述promise ajax封装形式)
// 发送请求
;(async function(){
let res=await ajax("http://vebcoder.cn:9527/api/getTypeOne")
console.log(res)//请求的数据结果
let res2=await ajax("http://vebcoder.cn:9527/api/goodList")
console.log(res2)//请求的数据结果
}())
async有更好的语义,几乎达到与同步代码一样的编程体验!
2019,不过是追求喜新厌旧的年头!
几个月前,我读了一篇关于rubygem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:
我需要一个表,其中行实际上是2行表,一个嵌套表是..我怎样才能在Prawn中做到这一点?也许我需要延期..但哪一个? 最佳答案 现在支持子表:Prawn::Document.generate("subtable.pdf")do|pdf|subtable=pdf.make_table([["sub"],["table"]])pdf.table([[subtable,"original"]])end 关于ruby-on-rails-PrawnPDF:Ineedtogeneratenested
网络编程套接字网络编程基础知识理解源`IP`地址和目的`IP`地址理解源MAC地址和目的MAC地址认识端口号理解端口号和进程ID理解源端口号和目的端口号认识`TCP`协议认识`UDP`协议网络字节序socket编程接口`sockaddr``UDP`网络程序服务器端代码逻辑:需要用到的接口服务器端代码`udp`客户端代码逻辑`udp`客户端代码`TCP`网络程序服务器代码逻辑多个版本服务器单进程版本多进程版本多线程版本线程池版本服务器端代码客户端代码逻辑客户端代码TCP协议通讯流程TCP协议的客户端/服务器程序流程三次握手(建立连接)数据传输四次挥手(断开连接)TCP和UDP对比网络编程基础知识
我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or
Devise是一个Ruby库,它为我提供了这个User类:classUser当写入:confirmable时,注册时会发送一封确认邮件。上周我不得不批量创建300个用户,所以我在恢复之前注释掉了:confirmable几分钟。现在我正在为用户批量创建创建一个UI,因此我需要即时添加/删除:confirmable。(我也可以直接修改Devise的源码,但我宁愿不去调和它)问题:如何即时添加/删除:confirmable? 最佳答案 WayneConrad的解决方案:user=User.newuser.skip_confirmation
我在Rails3.1项目中有以下助手-我只是想知道是否有办法测试CSV.generate调用。我很想说我知道如何去做,但事实是我什至不知道从哪里开始。任何想法表示赞赏。require'csv'moduleAdmin::PurchasesHelperdefcsv_purchase_listcolumns=['course','amount','first_name','last_name','contact_phone','contact_mobile','created_at']CSV.generate(:col_sep=>";",:row_sep=>"\r\n",:headers=>
我创建了一个由于“在运行时执行的单例元类定义”而无法编码的对象(这段代码的描述是否正确?)。这是通过以下代码执行的:#defineclassXthatmyusesingletonclassmetaprogrammingfeatures#throughcallofmethod:break_marshalling!classXdefbreak_marshalling!meta_class=class我该怎么做才能使对象编码正确?是否可以从对象instance_of_x的classX中“移除”单例组件?我真的需要一个建议,因为我们的一些对象需要通过Marshal.dump序列化机制进行缓存。
我在我的Rails项目中使用rspec_rails和factory_girl_railsgem。所有模型都已创建。是否有我可以运行的生成器来为现有模型创建工厂文件?例如:我已经有了一个Blog模型。RSpec允许我通过简单地运行以下命令在spec/models/blog_spec.rb生成一个模型规范文件:railsgeneraterspec:modelblog是否有我可以在命令行中运行的生成器,它会为这个现有模型生成工厂文件,位于:spec/factories/blogs.rb?我在factory_girl_rails中没有看到任何关于发电机的提及文档。
我正在查看Ruby日志记录库Logging.logger方法并从sourceatgithub提出问题与这段代码有关:logger=::Logging::Logger.new(name)logger.add_appendersappenderlogger.additive=falseclass我知道类 最佳答案 这实际上删除了方法(当它实际被执行时)。这是确保close不会被调用两次的保障措施。看起来好像有嵌套的“class 关于Ruby元编程问题,我们在StackOverflow上找到一
使用Paperclip,我想从这样的URL抓取图像:require'open-uri'user.photo=open(url)问题是我最后得到一个像“open-uri20110915-4852-1o7k5uw”这样的文件名。有什么方法可以更改user.photo上的文件名?作为一个额外的变化,Paperclip将我的文件存储在S3上,所以如果我可以在初始分配中设置我想要的文件名就更好了,这样图像就会上传到正确的S3key。像这样:user.photo=open(url),:filename=>URI.parse(url).path 最佳答案