js回调基于生成器的代码

时间:2016-04-30 10:45:32

标签: javascript node.js callback mocha generator

我想测试我的服务器。 测试提供了简单的场景:

  1. 向服务器发送请求
  2. 等待回复
  3. 检查一些内容
  4. 我尝试使用mocha进行测试,并使用supertest进行请求。

    测试示例:

    function request(url, query, cb) {
        var req = supertest(app.listen())
            .get(url)
            .query(query)
            .end(function(err, res){
                if (err) {throw (err);}
                cb(res);
            });
    }
    
    it('Check something after response', function *(done) {
        request(this.url, this.query, function(res) {/* some after response check here */});
    });
    

    现在我需要将我的回调式代码重组为生成器式代码。

    我需要这样的东西:

    it('Check something in response', function *(done) {
        var res = yield request(this.url, this.query);
        /* some after response check here */
    });
    

    不幸的是,我无法理解我需要在request()

    中做些什么改变

    P.S。我没有注意到具有适当风格的其他supertest类似解决方案。我只是想通过这个简单的例子来理解如何包装回调。

2 个答案:

答案 0 :(得分:1)

基本上,要将回调代码重写为生成器样式,您需要:

  • 使用yield someAsyncFunc(cb)
  • 的生成器函数
  • 通过调用此生成器创建的迭代器
  • 回调函数应该推进迭代器并设置其返回值iter.next(value)
  • 最后,要开始整个过程​​,你应该在迭代器上调用next一次

示例:

console.info=function(x){document.write('<pre>'+JSON.stringify(x,0,3)+'</pre>')}
//--


// some async function with a callback
function asyncFunc(done) {
    setTimeout(function() {
        done(Math.random())
    }, 500);
}

// generator
function *gen() {
    var val;
    val = yield asyncFunc(advanceIter);
    console.info(val)
    val = yield asyncFunc(advanceIter);
    console.info(val)
}

// create an iterator
iter = gen()

// define a callback for the async function
function advanceIter(value) {
    iter.next(value);
}

// get the whole machinery started
iter.next()

当然,在现实世界中,你将把你的发电机包裹在一个负责所有内务管理的功能中。您的生成器将接收advance参数,它应该盲目地进一步作为对它使用的异步函数的回调。

console.info=function(x){document.write('<pre>'+JSON.stringify(x,0,3)+'</pre>')}
//--

function asyncFunc(done) {
    setTimeout(function() {
        done(Math.random())
    }, 500);
}

function run(gen) {
    var iter = gen(function(value) {
        iter.next(value)
    });
    iter.next()
}

run(function *(advance) {
    var val;
    val = yield asyncFunc(advance);
    console.info(val)
    val = yield asyncFunc(advance);
    console.info(val)
});

请注意,异步函数本身不需要进行任何更改(在您的情况下为request)。

测试框架的一个例子:

// some async function we're going to test

function asyncFunc(param, cb) {
    setTimeout(function () {
        cb(param + '-ok');
    }, 500);
}

// classic async test

describe('async demo', function () {
    it('works', function (done) {
        asyncFunc('foobar', function (result) {
            expect(result).toBe('foobar-ok');
            done();
        })
    });
});

// generator test
// note that asyncFunc itself remains unchanged

function run(gen) {
    var iter = gen(function (value) {
        iter.next(value)
    });
    iter.next()
}

describe('yield demo', function () {
    it('works', function (done) {
        run(function *(advance) {
            var result = yield asyncFunc('barbaz', advance);
            expect(result).toBe('barbaz-ok');
            done();
        })
    });
});

// generator test 2
// adding more automation

function runGen(gen) {
    return function (done) {
        var iter = gen(function (value) {
            var r = iter.next(value);
            if (r.done)
                done();
        });
        iter.next();
    }
}

describe('yield demo 2', function () {
    it('works', runGen(function *(advance) {
        var result = yield asyncFunc('quux', advance);
        expect(result).toBe('quux-ok');
    }));
});

答案 1 :(得分:1)

怎么样:

function* request (url, query) {
    supertest(app.listen())
        .get(url)
        .query(query)
        .end((err, res) => {
            if (err) {
                throw err;
            }
            yield res;
        });
}

it('Check something after response', done => {
    const res = request(this.url, this.query).next();
    expect(res).to.be.defined
});