您所在的位置:首页 / 知识分享

异步的发展,一起学会怎么处理多请求

2019.02.21

3141

异步的发展,一起学会怎么处理多请求

  • 异步:先干一件事,中间去干其他的事,最终再回来干这件事
  • 高阶函数是函数作为参数或者函数作为返回值 ,作用是批量生成函数和预置函数做为参数(能够缓存函数,当到达条件时执行该函数)
  • 异步的开展流程: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)}) 复制代码

多恳求

其实假如看懂到这里,关于多恳求的完成也就不是大难事了。恳求能用fetch用fetch哈。
多恳求分为并发恳求(恳求之间没有关系,但需求拿到一切恳求结果)和串发恳求(后面恳求必需要拿到前面恳求的结果)。
关于并发恳求,觉得Promise.all处置更简单,串发恳求那就用await吧~



相关新闻

JavaScript 数据类型检测终极解决方案

2018.09.23

1101

JavaScript 的数据类型检测是我们平时开发中经常会遇到的场景,小到基本数据类型大至各种引用数据类型的检测,都是我们需要掌握的知识点。本章会详细讲解 JavaScript 中各种不同数据类型的检测方法以及最后会实现一个数据类型检测的终极方法。

PHP如何读取一个大文件txt

2018.12.06

594

文件已经一次性全部被载入到内存中并将文件的每一行保存到了一个php数组中,一次性载入这个202MB的文件file函数用了0.67秒钟、file_get_contents函数用了0.25秒钟(看起来file_get_content要比file靠谱的多