美文网首页
前端几种异步的处理方式

前端几种异步的处理方式

作者: 人话博客 | 来源:发表于2018-12-10 15:45 被阅读0次

    如果是单个ajax请求,不带有接口钱数据相互依赖的,其实怎么请求都没关系.

    jQuery,axios,vue-resource,XMLHttpRequest

    但一点有了接口间数据的相互依赖,那么问题就来了.

    由于,每一个请求都是独立的,且回调的时机不确定,为了保证请求接口数据返回的过程可控和相互依赖关系,可能需要费点周折.


    1. ajax callback

    $.get('url',function(data){
        $.get('url2' + data.id,function(data2){
            $.get('url3' + data2.id,func(data){
                // 拿到最终数据
            })
        })
    })
    

    使用最原始的方案,ajax callback 这种 回调函数嵌套的方式,在请求数据上面是没有问题的.在一个接口请求完毕之后,拿到数据,在接着请求下一个接口,很好理解,也很好书写.

    问题在于:

    当接口依赖多了之后,整个代码的嵌套层级就会变多,代码应该的竖向发展,变成了横向.
    这样可能会导致代码结构不清晰,不便于后期的维护.


    2. Promise

    function pget(url) { 
        return new Promise(function(reslove,reject){
            $.get(url,function(data){
                if (data.err) reject(data.err)
                resolve(data)
            })
        })
    }
    
    
    pget('url')
        .then(res=>{
            return pget('url2' + res.id)
        })
        .then(res=>{
            return pget('url3' + res.id)
        })
        .then(res=>{
            // 拿到最终数据
        })
    
    

    使用 then 确实能把callback 那种横向的趋势变成更加符合代码风格的纵向.

    问题在于:

    then 太多了,且语义化不强,前面的then也许还知道是干什么的,到了后面可能就懵逼了.


    3. Generator

    
    function *gen() {
        let data1 = yield $.ajax('url')
        let data2 = yield $.ajax('url2' + data1.id)
        yield $.ajax('url3') // 注意,在jQuery v3.0+版本,$.ajax() 支持了 promise
    }
    
    let g = gen()
    g.next().value
        .then(res=>{
            g.next(res.id).value
                .then(res=>{
                    g.next(res.id).value
                        .then(res=>{
                            // 拿到数据了,该干嘛干嘛.
                        })
                })
        })
    
    

    问题在于

    • 相比 $.ajax() 函数嵌套,变的更加复杂。
    • 相比 promise 属性变的更多。(.next().value)

    三种方式.

    1. 第一种 ajax callback 存在回调函数,结构是横向发展的.
    2. promise 确实结构变清晰了,在每一次的 reslove 返回一个新的 promise ,把这种接口依赖的操作用 then 纵向连接了起来.
    3. generator 就有点扯淡了,不光需要next() 还要 .value,更加扯淡的是,嵌套层级和 $.ajax() 没有区别,甚至于比 $.ajax() 写的代码更多了.

    我们所希望的

    不管是 $.ajax() callback 还是 promise then 或者是 generator .next().

    我们都希望都以同步的方式去写异步代码.

    let data = 异步请求数据(url)
    let data2 = 异步请求数据(url2 + data.id)
    let data3 = 异步请求数据(url3 + data2.id)
    
    console.log(data,data1,data2)
    
    

    由于 generator可以一次一次的拿到 promise,并且可以暂停执行.

    可以根据 generator 的这个特性来实现以同步代码的写法来执行异步任务

    function readFilePromise(path) {
      return new Promise(function (reslove, reject) {
        fs.readFile(path, 'utf8', (err, data) => {
          if (err) reject(err)
          reslove(data)
        })
      })
    }
    
    
    function* gen2() {
      let data1 = yield readFilePromise('./1.txt')
      console.log(`data1:${data1}`)
      let data2 = yield readFilePromise('./2.txt')
      console.log(`data2:${data2}`)
    }
    

    此段代码,正常情况下,每一次 next() 拿到了的对象 .value 都是一个 promise.

    核心思想是,我们等待 promse 对象,执行完毕了,在继续执行下一个 yield .

    function genRunner(gen) {
        let g = gen() // 拿到迭代器指针
        function next(data) {
            let nextObj = g.next(data)
            if (!nextObj.done) { // 如果迭代没有完成
                // 等待当前这个 promise执行完成了.
                nextObj.value.then(res=>{
                // 继续下一次的迭代.并把结果传递给当前的next()方法.
                  next(res)  
                })
            }
        }
        
        next() // 第一此调用第一个 yield ,第一个yield没办法接受参数.
    }
    
    

    测试一下:

    genRunner(gen2)
    

    结果:

    data1:1111111111
    data2:22222222222
    

    所以,我们可以利用 generator & runner 的工具,在 generator 中像写同步代码那样书写异步代码.

    genRunner 函数到底干了些什么事情?

    • 首先,generator 我们是可以手动调用 next() 一步步执行的.
    • genRunner 首选拿到 迭代器指针,指向第一个 next() 返回的obj
    • 根据这个obj.done判断迭代器是否遍历完毕.
      • 如果没有遍历完毕,

        • 就先拿到这个obj.value ,也就是 promise 对象,执行它的then.
        • 等待这个 promisethen 执行完毕之后,再次手动调用 next()
      • 如果执行完毕了,就什么也不做.


    常用的工具还有 co

    npm i co
    
    co(gen2)
    
    data1:1111111111
    data2:2222222222
    

    问题在于

    如果我们非要使用 generator 发送异步请求的话,那么为了不写恶心的嵌套代码,就需要借助第三方 co,runner 这样的插件.(或者自己写一个)


    使用 ES7 推出的 async / await

    function readFilePromise(path) {
      return new Promise(function (reslove, reject) {
        fs.readFile(path, 'utf8', (err, data) => {
          if (err) reject(err)
          reslove(data)
        })
      })
    }
    
    
    async function readFile() {
        let res1 = await readFilePromise('./1.txt')
        let res2 = await readFilePromise('./2.txt')
    }
    

    结果:

    data1:1111111111
    data2:2222222222
    

    async & await 基本是个语法糖,它结合了 generator & 类似 runner | co 的功能,能让我们很舒服的用同步代码的方式写异步代码.

    相关文章

      网友评论

          本文标题:前端几种异步的处理方式

          本文链接:https://www.haomeiwen.com/subject/hntdhqtx.html