generator 和异步回调

在我们日常的前端开发中,异步回调是不可避免的操作,会严重影响我们的编程体验和代码的维护性、阅读性,而 promise 和 generator 可以在一定程度上解决这个问题。它们没有那么神奇,只是代码上的一些小花招,现在我们具体来看看 generator 是如何解决异步回调的问题。

一般的异步回调写法

我们先来看看,ajax 请求的一般写法。

var mysite = 'zengxiaoluan.com';

function ajax (url, callback) {
    setTimeout(function (url, callback) {
        callback(url)
    }, 1000, url, callback)
}
// bad way
ajax('other.com', function (url) {
    if (url === mysite) {
        console.log('is my site')
    } else {
        console.log('is not my site')
        ajax('zengxiaoluan.com', function (url) {
            console.log(url)
        })
    }
})

上面的代码大致模拟了一个异步请求,一秒之后函数会返回当前请求的 url,如果这个 url 是我的网站则停止请求,否则将继续请求。也许用“请求”这种字眼并不合适,但是在实际开发中我们经常遇到类似的情况,我们得根据上一次的返回结果做下一次的操作。按照传统的调用方式,我们就会产生回调地狱的问题(有没有想到十八层地域这个词?😏)。

generator

我们来看看 generator 如何解决上面的问题,使用同步的方式写异步的代码。直接上代码。

function sync (generator) {
    // generate a iterator
    var task = generator();
    // start excute
    var result = task.next();
    
    function step () {
        if (!result.done) {
            if (typeof result.value === 'function') {
                result.value(function (url) {
                    result = task.next(url)
                    step();
                })
            } else {
                result = task.next(result.value)
                step();
            }
        }
    }

    step();
}

function wrapAjax(url) {
    return function (callback) {
        ajax(url, callback);
    }
}

sync(function *() {
    var url = yield wrapAjax('other.com');
    console.log(url);

    if (url == mysite) {
        return console.log('is my site')
    }

    var url2 = yield wrapAjax('zengxiaoluan.com');

    if (url2 == mysite) {
        return console.log('is my site')
    }
})

上面的代码,我们先封装了一个 sync 函数,参数为一个生成器函数。在生成器里我们分别调用了两次 wrapAjax 这个方法去做异步操作······

哎呀,文风突转,感觉写不下去了,大家自己在最新的谷歌浏览器里运行代码。就此打住,我先去吃饭了。

2020-03-30 更新

学习英语是理解编程的第一步,generator 有发电机的意思,而 yield 有产生的意思,两者结合可以源源不断产生电源,很符合生成器函数的特性。而利用 generator 处理异步也可以这样写:

  function* generator() {
    let init = 10;
    let a = yield setTimeout(() => {
      g.next(init);
    }, 1000);
    console.log(a);

    let b = yield new Promise(function(res, rej) {
      setTimeout(() => {
        g.next(a + 1);
      }, 1000);
    });
    console.log(b);

    let c = yield setTimeout(() => {
      g.next(b + 1);
    }, 1000);
    console.log(c);
  }
  let g = generator();
  console.log(g.next());

参考链接

  • function *
  • Contains code and snippets for Functional Programming in Javascript.

作者: 曾小乱

喜欢写点有意思的东西

《generator 和异步回调》有一个想法

评论已关闭。