I was suggested by @Bergi on StackOverflow that Async Promise constructors are any pattern.
The argument behind this is that async Promise swallows the errors. In my case, I have try..catch so I'm not sure if the argument is valid. But here is my code - the core of the Wayne JavaScript library.
export class HTTPResponse {
constructor(resolve, reject) {
this._resolve = resolve;
this._reject = reject;
}
html(data, init) {
this.send(data, { type: 'text/html', ...init });
}
text(data, init) {
this.send(data, init);
}
json(data, init) {
this.send(JSON.stringify(data), { type: 'application/json', ...init });
}
blob(blob, init = {}) {
this._resolve(new Response(blob, init));
}
send(data, { type = 'text/plain', ...init } = {}) {
if (![undefined, null].includes(data)) {
data = new Blob([data], {
type
});
}
this.blob(data, init);
}
// .. there are more code that are irrelevant
}
function chain_handlers(handlers, callback) {
if (handlers.length) {
return new Promise((resolve, reject) => {
let i = 0;
(async function recur() {
const handler = handlers[i];
if (!handler) {
return resolve();
}
try {
await callback(handler, function next() {
i++
recur();
});
} catch(error) {
reject(error);
}
})();
});
}
}
export class Wayne {
constructor() {
this._er_handlers = [];
this._middlewares = [];
this._routes = {};
this._timeout = 5 * 60 * 1000; // 5 minutes
this._parser = new RouteParser();
self.addEventListener('fetch', (event) => {
const promise = new Promise(async (resolve, reject) => {
const req = event.request;
try {
const res = new HTTPResponse(resolve, reject);
await chain_handlers(this._middlewares, function(fn, next) {
return fn(req, res, next);
});
const method = req.method;
const url = new URL(req.url);
const path = normalize_url(url.pathname);
const routes = this._routes[method];
if (routes) {
const match = this._parser.pick(routes, path);
if (match.length) {
const [first_match] = match;
const fns = [...this._middlewares, ...routes[first_match.pattern]];
req.params = first_match.data;
setTimeout(function() {
reject('Timeout Error');
}, this._timeout);
await chain_handlers(fns, (fn, next) => {
return fn(req, res, next);
});
return;
}
}
if (event.request.cache === 'only-if-cached' && event.request.mode !== 'same-origin') {
return;
}
//request = credentials: 'include'
fetch(event.request).then(resolve).catch(reject);
} catch(error) {
this._handle_error(resolve, req, error);
}
});
event.respondWith(promise.catch(() => {}));
});
['GET', 'POST', 'DELETE', 'PATCH', 'PUT'].forEach(method => {
this[method.toLowerCase()] = this.method(method);
});
}
_handle_error(resolve, req, error) {
const res = new HTTPResponse(resolve);
if (this._er_handlers.length) {
chain_handlers(this._er_handlers, function(handler, next) {
handler(error, req, res, next);
}, function(error) {
res.html(...error500(error));
});
} else {
res.html(...error500(error));
}
}
use(...fns) {
fns.forEach(fn => {
if (typeof fn === 'function') {
if (fn.length === 4) {
this._er_handlers.push(fn);
} else if (fn.length === 3) {
this._middlewares.push(fn);
}
}
});
}
method(method) {
return function(url, fn) {
if (!this._routes[method]) {
this._routes[method] = {};
}
const routes = this._routes[method];
if (!routes[url]) {
routes[url] = [];
}
routes[url].push(fn);
return this;
};
}
}
The library works like Express.js in NodeJS but for Service Worker in browser. You can have a middleware or get/post handlers:
const app = new Wayne();
app.use((req, res, next) => {
});
app.get((req, res) => {
res.json({foo: 'bar'});
});
and the user can call res.json()
(and other methods) to resolve the promise from the code above. The code needed a Promise constructor to resolve it in different places and times (by the user). And async was added to simplify calling the chain_handlers
function and waiting for it to finish.
The whole code can be seen on GitHub: index.js.
I need help with refactoring this code to get rid of the async promise (if possible) and not make the code a lot of complicated than it is.
next()
callbacks? \$\endgroup\$async
functions. More recent ("modern") libraries take a better approach. See blog.logrocket.com/node-js-alternative-frameworks-express-js for a good comparison. \$\endgroup\$