前端几种异步的处理方式

如果是单个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 的功能,能让我们很舒服的用同步代码的方式写异步代码.

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 230,362评论 6 544
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 99,577评论 3 429
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 178,486评论 0 383
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 63,852评论 1 317
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 72,600评论 6 412
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 55,944评论 1 328
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 43,944评论 3 447
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 43,108评论 0 290
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 49,652评论 1 336
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 41,385评论 3 358
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 43,616评论 1 374
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 39,111评论 5 364
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 44,798评论 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 35,205评论 0 28
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 36,537评论 1 295
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 52,334评论 3 400
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 48,570评论 2 379

推荐阅读更多精彩内容

  • 异步编程对JavaScript语言太重要。Javascript语言的执行环境是“单线程”的,如果没有异步编程,根本...
    呼呼哥阅读 7,325评论 5 22
  • 弄懂js异步 讲异步之前,我们必须掌握一个基础知识-event-loop。 我们知道JavaScript的一大特点...
    DCbryant阅读 2,746评论 0 5
  • 一、Javascript实现异步编程的过程以及原理 1、为什么要用Javascript异步编程 众所周知,Java...
    Ebony_7c03阅读 869评论 0 2
  • Prepending(进行时),Resolve(成功了),Reject(失败了),then......等 1.Pr...
    _菩提本无树_阅读 49,149评论 0 21
  • 一棵树爱上了马路对面另一颗树。然后就没有然后了,有些事,一开始就是结束。
    lnhy阅读 135评论 0 0