异步的发展,顺手学会怎么处理多请求

栏目: 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)})

复制代码

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

工程问题C++语言求解

工程问题C++语言求解

Delores M.Etter、Jeanine A.Ingber / 冯力、周凯 / 机械工业出版社 / 2014-8 / 79元

本书介绍了如何利用ANSIC++编程语言以基于对象的编程方式来解决工程问题。书中引用了大量来自于不同工程、科学和计算机科学领域的示例,是一本理论和实践结合紧密的教材。针对C++基本语法的各个部分,由浅入深地进行讲解。每讲解一部分基础知识,同时会结合多个相关实例,实例内容详实,紧贴所讲内容,使读者能够立刻对所学知识进行练习,实战性强。一起来看看 《工程问题C++语言求解》 这本书的介绍吧!

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换