18 ms·
“requests” doesn’t support (or apparently want to support) Promises which are now widely used in the JavaScript community, thus it’s obsolete. Promises work bes
by tlrobinson 8y ago
“requests” doesn’t support (or apparently want to support) Promises which are now widely used in the JavaScript community, thus it’s obsolete. Promises work best when all the async operations in your code use them, especially with async/await.
Interestingly early versions of Node had a limited form of Promises but they were quickly removed.
- matthewaveryusa 8y agoJust to give a little bit more context, in contemporary javascscript you can write something like this (wrapped in a try-catch: async function http_request_new(url) { return new Promise(...) } const http_response = await http_request_new("https://www.google.com") ... The await signals that the asynchronous http_request function is non-blocking, and we should continue execution once the non-blocking return value (promise) has resolved. Previously idiomatic javascript would have looked like this: function http_request_old(url, cb) { return ... } http_request_old("https://www.google.com", function(error, http_response) { ... }) Luckily node.js provides util.promisify that converts any old callback-style async function into one that returns a promise, so that await can be used: http_request_new = util.promisify(http_request_old) const http_response = await http_request_new("https://www.google.com") ... Unless I'm mistaken, it's fairly trivial to promisify the old-style code
- javagram 8y agoIn the case of Request, there is already an official wrapper for the library that supports promises. https://github.com/request/promise-core https://github.com/request/promise-core It is a couple hundred lines of code though.
- Cthulhu_ 8y agoExactly - for the longest time, the success / error callback signature was the primary and expected way to handle async code in Node modules, and there was a lot of objection to using promises - in part because at the time it was not stable, and in part because its performance was less compared to callback style. You don't want to put performance bottlenecks in core and/or frequently used libraries just so it fits your preferred async style. If you REALLY want to pay that price, it's trivial to promisify a library like this - and it doesn't add complexity or size or (worst case) dependencies (like 3rd party promise libraries, as was the case for a long time) to your small, focused library.
- deleted 8y ago[deleted]
- jrochkind1 8y agoi guess these are the downsides of a platform without much stdlib. consensus stdlib doesn't really work, and then things don't work together.
- Klathmon 8y agoutil.promisify won't work on request in most places because they don't use the defacto standards of callbacks (the first argument being an error argument, callback being the last argument, etc...). Funnily enough, i believe it predates the widespread use of that convention! But that means you need to either do it yourself (which admittedly is pretty trivial), or use custom bindings to "promisify" request (which do exist! and are even maintained by the original authors). But promises are only one reason why request is more difficult to use. Compare the node.js style streams with async iteration which could be possible if the library supported it: node streams style: const readStream = fs.createReadStream(inputFilePath, { encoding: 'utf8', highWaterMark: 1024 }); readStream.on('data', (chunk) => { console.log('>>> '+chunk); }); readStream.on('end', () => { console.log('### DONE ###'); }); async iteration streams: for await (const chunk of fs.createReadStream(inputFilePath, { encoding: 'utf8', highWaterMark: 1024 });) { console.log('>>> ' + chunk) } console.log('### DONE ###') Not only is the latter easier to quickly grep and understand, but it also is easier to catch exceptions (a try/catch works, and it bubbles up! So it's harder to ignore errors by forgetting to add a `readStream.on('error'...)`), and it works correctly in async contexts so it doesn't also need to be wrapped by Promise constructors. Then throw in writeable streams, transform streams, and tons more that all require more difficult setup, less standard ways of working, and are overall harder to get completely right without any bugs.
- z3t4 8y agoWith async/await you have to wrap everything in try/catch, so it only looks better if you do not handle errors. It also makes it harder to do more advanced designs like rate limiting, loading bars, etc. I really like callback convention with error first. Something being async also means it can fail. The most important part of async code is error handling.
- Klathmon 8y agoerrors bubble in async contexts still, so this example will still catch errors correctly: async function foo () { throw new Error ('test') } async function bar () { return foo() } async function baz () { try { await bar() } catch (err) { console.error(err.message) } } That's not the case in callback-based systems where it's more of a golang style "handle it right then, or it gets ignored forever" kind of thing. But also unhandled promise rejection handlers are seeing much more widespread usage now to catch "unhandled promise rejections", which make it a bit easier to handle errors in async contexts, but i'll be the first to agree that there is still a lot that sucks about handling errors in async contexts in javascript... > It also makes it harder to do more advanced designs like rate limiting, loading bars, etc. It does, but you can also handle a lot of these by mixing promises and async/await (since a/a is basically a nicer syntax for Promises). And callbacks still exist for the cases that really need them (like you said, loading callbacks are still a hell of a lot easier to use than janky promise-based solutions in most cases in my opinion! Mostly because a promise is one-and-done, but callbacks can be re-called multiple times). But the point is that if you stick to ONLY callbacks, EVERYTHING has to handle wrapping it. Whereas if you move on to using the new features where they make sense (the "easy path" can use async/await or async iterator streams), then only people who need those extra complicated features have to wrap and work with those more complicated setups.
- lucideer 8y ago> Unless I'm mistaken, it's fairly trivial to promisify the old-style code Even if it was trivial to promisify request (though it seems it isn't according to sibling comments), the point is that the promisify utility is effectively a workaround for working with older libs & APIs in a modern idiomatic way. It's supposed to be a temporary shim to move into modern patterns, not something whereby you start a new project, choose new actively-developed lib, and expect to have to wrap that lib just to use it idiomatically. Similarly, "upgrading" your popular lib to have a modern API just by wrapping it with the promisify util and leaving the internals using a legacy pattern not only adds overhead, but it also just a bit of a hack, and not very ideal. Especially when more modern alternatives to your lib exist which you could be recommending.
- sjustinas 8y agoThere is an [official] additional package "request-promise" that adds Promise support. I don't dabble in Node much, but is this a bad thing? Although on the other hand, most other libraries support promises out of the box (e.g. using the "pass no callback to get a Promise back" pattern).
- nailer 8y agoThere's also axios, superagent, and a whole bunch of excellent node HTTP clients (also 'fetch' if you like encoding URLs and parsing JSON manually).
- Klathmon 8y agoBolting on the newer features as additional packages has a few downsides in my experience: * Increased complexity. So now in order to use request in a modern JS ecosystem, you need to install request, and request-promise-native, which itself installs request-promise-core, which all 3 then combine to use. Which do you look for to find documentation? Which do you look at when you have bugs? How does interop happen with other 3rd party libraries? Does every other lib that works with request have to handle all the official wrappers or just the main? And good god what happens when breaking changes are made to one of them? Everything is just a lot more complicated here. * Only so much can be easily patched on. Things like streams are pretty complicated, and writing a complicated (and often slow) runtime transformation of a stream to an async iterator, both of which don't have complete 100% matches for all of their features, means that everything is now using a sub-par incomplete interface. The main application can't implement features which take advantage of the new syntax, and the wrapper can't always represent the old ways of working perfectly. Basically, it's a leaky abstraction in most cases, which is slower, uses more memory, and is a lot more complex.
- mwilliaams 8y agoI use request-promise and it’s always worked perfectly fine. I normally do const response = await rp({ request params }); and then continue on.