异步的发展,顺手学会怎么处理多请求
栏目: 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异步请求服务器数据?
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Filter Bubble
Eli Pariser / Penguin Press / 2011-5-12 / GBP 16.45
In December 2009, Google began customizing its search results for each user. Instead of giving you the most broadly popular result, Google now tries to predict what you are most likely to click on. Ac......一起来看看 《The Filter Bubble》 这本书的介绍吧!