Open olegsmetanin opened 3 weeks ago
This is how I do it:
.addFilter('await', function (promise: Promise<any>, callback: (err: Error | null, result?: any) => void) {
promise.then(result => {
callback(null, result);
}).catch(err => {
callback(err);
});
}, true);
@geleto it doesn't work for any real async case, just try to send setTimeout promise with 0 delay to this filter. I expect the following filter will work and render "result":
.addFilter('await', function (_in: any, callback: (err: Error | null, result?: any) => void) {
const promise = new Promise((resolve) => {
setTimeout(() => {
resolve('result')
}, 0)
});
promise.then(result => {
callback(null, result);
}).catch(err => {
callback(err);
});
}, true);
Here are some example tests for real async cases:
import { expect } from 'chai';
import * as nunjucks from 'nunjucks';
describe('await filter tests', () => {
let env: nunjucks.Environment;
beforeEach(() => {
env = new nunjucks.Environment();
// Implement 'await' filter for resolving promises in templates
env.addFilter('await', function (promise: Promise<any>, callback: (err: Error | null, result?: any) => void) {
promise.then(result => {
callback(null, result);
}).catch(err => {
callback(err);
});
}, true);
});
it('should handle custom async filter with global async function', (done) => {
// Add global async function
env.addGlobal('fetchUser', async (id: number) => {
// Simulate async operation
await new Promise(resolve => setTimeout(resolve, 10));
return { id, name: `User ${id}` };
});
const template = '{% set user = fetchUser(123) | await %}Hello, {{ user.name }}!';
env.renderString(template, {}, (err, result) => {
if (err) return done(err);
expect(result).to.equal('Hello, User 123!');
done();
});
});
it('should handle asyncEach with array of promises', (done) => {
// Add global function to fetch records
env.addGlobal('getRecords', () => {
// Return an array of promises (each record is a promise)
return [
new Promise(resolve => setTimeout(() => resolve('Record 1'), 10)),
new Promise(resolve => setTimeout(() => resolve('Record 2'), 20)),
new Promise(resolve => setTimeout(() => resolve('Record 3'), 15))
];
});
const template = `{%- set records = getRecords() -%}
{%- asyncEach rec in records -%}
{{ rec | await }}{% if not loop.last %}\n{% endif %}
{%- endeach %}`;;
env.renderString(template, {}, (err, result) => {
if (err) return done(err);
expect((result as string).trim()).to.equal('Record 1\nRecord 2\nRecord 3');
done();
});
});
});
@geleto Thank you! It seems that we can't call renderString without callback if we have async filters
import { expect } from 'chai';
import * as nunjucks from 'nunjucks';
describe('await filter tests', () => {
let env: nunjucks.Environment;
beforeEach(() => {
env = new nunjucks.Environment();
env.addFilter('fetchUser', async (id: string, callback: (err: Error | null, result?: any) => void) => {
// Simulate async operation
const promise = new Promise(resolve => setTimeout(() => resolve(`User ${id}`), 10));
promise.then(result => {
callback(null, result);
}).catch(err => {
callback(err);
});
}, true);
});
it('should render using renderString without callback', () => {
const template = '{% set user = "User 123" %}Hello, {{ user }}!';
const result = env.renderString(template, {});
expect(result).to.equal('Hello, User 123!');
});
it('should handle custom async filter using renderString with callback', (done) => {
const template = '{% set user = "123" | fetchUser %}Hello, {{ user }}!';
env.renderString(template, {}, (err, result) => {
if (err) return done(err);
expect(result).to.equal('Hello, User 123!');
done();
});
});
// Failed!
it('should handle custom async filter using renderString without callback', () => {
const template = '{% set user = "123" | fetchUser %}Hello, {{ user }}!';
const result = env.renderString(template, {});
expect(result).to.equal('Hello, User 123!');
});
});
await filter tests
✔ should render using renderString without callback
✔ should handle custom async filter using renderString with callback
1) should handle custom async filter using renderString without callback
2 passing (24ms)
1 failing
1) await filter tests
should handle custom async filter using renderString without callback:
AssertionError: expected null to equal 'Hello, User 123!'
"nunjucks": "^3.2.4",
Async filter
with template
doesn't work and renders nothing