js回调到基于生成器的代码

js callbacks to generator based code

本文关键字:代码 于生成 回调 js      更新时间:2023-09-26

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

  1. 将请求发送到服务器
  2. 等待响应
  3. 检查一下

我尝试使用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() 中做什么更改

附言:我不关注其他风格合适的类似supertest的解决方案。我只想了解如何用这个简单的例子包装回调。

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

  • 使用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');
    }));
});

怎么样:

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
});