异步的发展,顺手学会怎么处理多请求
栏目: JavaScript · 发布时间: 5年前
内容简介:普通的读到2个文件之后才能进行某件事,可能最开始的手段:先介绍高阶函数的含义和用法。比如判断变量是不是对象或者数组
- 异步:先干一件事,中间去干其他的事,最终再回来干这件事
- 高阶函数是函数作为参数或者函数作为返回值 ,作用是批量生成函数和预置函数做为参数(可以缓存函数,当达到条件时执行该函数)
- 异步的发展流程:callback -> promise -> generator + co -> async+await(语法糖)
- 回调函数金字塔处理多请求 -> 哨兵函数处理多请求 -> promise处理多请求 -> co处理多请求 -> async处理多请求
- 手写实现co、bluebird的promisify和promisifyAll
异步的发展流程
-
异步:先干一件事 中间去干其他的事,最终在回来干这件事
-
同步:同步连续执行
-
异步的发展流程:callback -> promise -> generator + co -> async+await(语法糖)
-
异步发展的最终结果就是,像同步一样的写法,简单优雅易懂
回调函数的金字塔地狱版本1.0
普通的读到2个文件之后才能进行某件事,可能最开始的手段:
// 本地写3文件,index.js写以下代码 template.txt写些html的代码,data.txt写些json数据,然后命令行运行 node index.js let fs = require('fs') fs.readFile('template.txt','utf8',function(err,template){ // error-first fs.readFile('data.txt','utf8',function(err,data){ // error-first console.log({template:template,data:data}); }); }); 复制代码
理解高阶函数
先介绍高阶函数的含义和用法。
-
含义:函数作为参数或者函数作为返回值
-
用法:批量生成函数和预置函数做为参数
批量生成函数
比如判断变量是不是对象或者数组
function isObject(content){ return Object.prototype.toString.call(content) === '[object Object]'; } function isArray(content){ return Object.prototype.toString.call(content) === '[object Array]'; } 复制代码
但这样一个个写很麻烦,可以写一个函数生成这些函数,这样简单粗暴。在平时你发现函数里有重复代码的时候,可以考虑封装一个高阶函数生成函数~
function isType(type){ return function(content){ return Object.prototype.toString.call(content) === `[object ${type}]`; } } const isObject = isType('Object') const isArray = isType('Array') 复制代码
预置函数做为参数
lodash里面有个after的函数,功能是函数调用几次之后才真正执行函数,很神奇是吧,走一个~
function after(times,fn){ return function(){ if(--times===0){ fn() } } } let eat = after(3,function(){ console.log('饱了') }) eat(); eat(); eat(); // 这次才会执行 复制代码
举一反三,换句话说这样可以缓存函数,当达到条件时执行该函数。这就超级厉害了~
高阶函数的哨兵变量版2.0
由上面例子得到的启发,再看前面的例子
// 本地写3文件,index.js写以下代码 template.txt写些html的代码,data.txt写些json数据,然后命令行运行 node index.js function after(requestCounts,fn){ let dataSet = {} // 数据收集,请求跟结果一一对应,所以存为对象,这个变量通常称为哨兵变量 // return的函数就是单个读取到结果之后在其回调函数里执行的函数,所以可以拿到数据 return function(key,data){ dataSet[key] = data // 所有请求都拿到结果之后 if(Object.keys(dataSet).length ===requestCounts){ fn(dataSet) } } } let out = after(2,function(res){ console.log(res); }) let fs = require('fs') fs.readFile('template.txt','utf8',function(err,data){out('template',data)}) fs.readFile('data.txt','utf8',function(err,data){out('data',data)}); 复制代码
这样很方便处理并发请求,请求的数量传入即可。
理解promise
可以对照promise的网站,自己试着实现promise。
// 大概用法 var y = new Promise((resolve,reject)=>{ setTimeout(()=>{ let x = Math.random() if(x >0.5){ resolve(x) }else{ reject(x) } },100) }) console.log(y) var yThen = y.then((data)=>{ console.log('then',data) },(data)=>{ console.log('catch',data) }) 复制代码
promise版本3.0
let fs = require('fs') function readFilePro(filename){ return new Promise((resolve,reject)=>{ fs.readFile(filename,'utf8',function(err,data){err?reject(err):resolve(data)}); }) } Promise.all([readFilePro('template.txt'),readFilePro('data.txt')]).then(res=>{ console.log({template:res[0],data:res[1]}) }) 复制代码
理解生成器generator
生成器函数虽然是一个函数,但和普通函数不一样,普通函数一旦调用就会执行完。
- 生成器函数用* 来标识
- 调用的结果是一个迭代器 迭代器有一个next方法
- 遇到暂停点yield就停下来,直到执行迭代器的next,最后才能返回这个函数的return
- yield后面跟着的是value的值
- yield等号前面的是我们当前调用next传进来的值
- 第一次next传值是无效的
- 当done为true的时候就是value就是生成器return的值
// 生成器函数有个特点需要加个* function *go(a){ console.log(1) // 此处b是外界输入,这行代码实现输入输出 let b = yield a console.log(2) let c = yield b console.log(3) return 'o' } // 生成器函数和普通函数不一样调用他函数不会立刻执行 // 返回生成器的迭代器,迭代器是一个对象 let it = go('a') // next第一次执行不需要传参数,想想也是,没有意义 let r1 = it.next() console.log(r1) // {value:'a',done:false} let r2 = it.next('B') console.log(r2) // {value:'B',done:false} let r3 = it.next('C') // 当done为true的时候就是return的值 console.log(r3) // {value:'o',done:true} 复制代码
理解co,让生成器自动执行
co是 大神tj 写出来的,超棒的小伙子啊,才23岁好像,再次感慨人与人之间的差距简直比人与狗之间的差距还大,面条泪~
co让生成器自动执行的原理其实想想就是让next运行到结束为止。
!!!!必须特别强调: co 有个使用条件,generator 函数的 yield 命令后面,只能是 Thunk 函数或 Promise 对象。
// gen是生成器generator的简写 function co(gen){ let it = gen() return new Promise((resolve,reject)=>{ !function next(lastValue){ let {value,done} = it.next(lastValue) if(done){ resolve(value) }else{ // 递归,这里也看出来,这也是为啥yield后面必须是promise类型 value.then(next) } }() }) } co(go) 复制代码
promise和co版本的4.0
// 本地写3文件,index.js写以下代码 template.txt写些html的代码,data.txt写些json数据,然后命令行运行 node index.js let fs = require('fs') function readFilePro(filename){ return new Promise((resolve,reject)=>{ fs.readFile(filename,'utf8',function(err,data){err?reject(err):resolve(data)}); }) } function *gen(){ // let res = {} let template = yield readFilePro('template.txt') let data = yield readFilePro('data.txt') return {template,data} } // 也可以直接引入 co的库 npm i co let co = require('co') function co(gen){ let it = gen() return new Promise((resolve,reject)=>{ !function next(lastValue){ let {value,done} = it.next(lastValue) if(done){ resolve(value) }else{ // 递归,这里也看出来,这也是为啥yield后面必须是promise类型 value.then(next) } }() }) } co(gen).then(res=>console.log(res)) 复制代码
async和await版本5.0
async和await是promise和generator的语法糖。其实 go 函数就是gen函数里面的yield变成await~
因为async函数其实有点co的感觉,await后面必须是promise~
// 本地写3文件,index.js写以下代码 template.txt写些html的代码,data.txt写些json数据,然后命令行运行 node index.js let fs = require('fs') // readFilePro也可以用bluebird生成 function readFilePro(filename){ return new Promise((resolve,reject)=>{ fs.readFile(filename,'utf8',function(err,data){err?reject(err):resolve(data)}); }) } async function go(){ let template = await readFilePro('template.txt') let data = await readFilePro('data.txt') // 这里的return必须用then才能拿到值,因为是语法糖啊~ return {template,data} } go().then(res=>console.log(res)) 复制代码
这也是最终版啦,异步写成同步的感觉~
bluebird
再叨叨点bluebird,它能把任意通过回调函数实现的异步API换成promiseApi。
常用的方法两个:promisify和promisifyAll。
promisify将回调函数实现的异步API换成promiseApi。
promisifyAll遍历对象上所有的方法 然后对每个方法添加一个新的方法 Async。
let fs = require('fs') // npm i bluebird let Promise = require('bluebird') let readFilePro = Promise.promisify(fs.readFile) // 好像很眼熟是不是 哈哈哈哈 readFilePro('template.txt','utf8').then((template)=>{console.log(template)}) Promise.promisifyAll(fs) // console.log(fs) // 发现fs的方法多了 fs.readFileAsync('template.txt','utf8').then((template)=>{console.log(template)}) 复制代码
其实感觉可以手写实现的有木有,来走一个~
let fs = require('fs') // 先看简单版的 function readFilePro(filename,encode){ return new Promise((resolve,reject)=>{ fs.readFile(filename,encode,function(err,data){err?reject(err):resolve(data)}); }) } // 高阶函数生成上面的函数 function promisify(fn){ // 这里生成readFilePro类似的函数,这里因为参数不一定,所以用args return function(...args){ return new Promise((resolve,reject)=>{ // 因为回调函数在最后一个,所以用拼接的方式,call的用法知道哈~ fn.call(null,...args,function(err,data){err?reject(err):resolve(data)}) }) } } function promisifyAll(object){ for (const key in object) { if (object.hasOwnProperty(key) && typeof object[key]==='function') { object[`${key}Async`] = promisify(object[key]) } } return object } let readFilePro = promisify(fs.readFile) // 好像很眼熟是不是 哈哈哈哈 readFilePro('template.txt','utf8').then((template)=>{console.log(template)}) promisifyAll(fs) // console.log(fs) fs.readFileAsync('template.txt','utf8').then((template)=>{console.log(template)}) 复制代码
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 原生JS发送异步数据请求
- SpringBoot 教程之处理异步请求
- 四步轻松实现ajax发送异步请求
- 关于ajax异步请求的一个细节问题
- easyhttp v1.1发布,新增异步并发请求
- 如何优化Android异步请求服务器数据?
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
活着就为改变世界
[美] 杰弗里·扬、威廉·西蒙 / 蒋永军 / 中信出版社 / 2010-6 / 39.00元
内容简介 苹果公司CEO史蒂夫•乔布斯这个一直活在自己想象的世界里的创业奇才,经历过各种挫折与失落,但他那无所畏惧、敢于承担的个性让他一直努力实践着自己的价值观,总能为他的离奇想法找到解决问题的办法。 本书两位作者通过深入访谈和资料调查,揭秘了许多乔布斯个人的创业经历和家庭变故,为大家塑造了一个活生生的“乔布斯式”的鲜活人物,描述了一个个充满传奇色彩的商业奇迹,真实地再现了乔布斯几十年......一起来看看 《活着就为改变世界》 这本书的介绍吧!