then()
and catch()
. For example, we have an API with three methods: getItem()
, updateItem()
, and deleteItem()
, each of which returns a promise: Promise.resolve() .then(_ => { return api.getItem(1) }) .then(item => { item.amount++ return api.updateItem(1, item); }) .then(update => { return api.deleteItem(1); }) .catch(e => { console.log('error while working on item 1'); })
then()
call creates another step in the chain of promises. If an error occurs anywhere in the chain, the catch()
block is called, which is located behind the failed section. The then()
and catch()
methods can either return a value or a new promise, and the result will be passed to the next then()
operator in the chain. api.getItem(1, (err, data) => { if (err) throw err; item.amount++; api.updateItem(1, item, (err, update) => { if (err) throw err; api.deleteItem(1, (err) => { if (err) throw err; }) }) })
fs.readFile
function based on callbacks into a function that uses promises: function readFilePromise(filename) { return new Promise((resolve, reject) => { fs.readFile(filename, 'utf8', (err, data) => { if (err) reject(err); else resolve(data); }) }) } readFilePromise('index.html') .then(data => console.log(data)) .catch(e => console.log(e))
Promise
designer. It takes a function, which, in turn, has two parameters - resolve
and reject
, which are also functions. Inside this function, all the work is done, and when we complete it, we call resolve
if successful, and reject
if an error has occurred.resolve
or reject
, and this call must be executed only once. In our example, if fs.readFile
returns an error, we pass this error to reject
. Otherwise, we pass the file data to resolve
.Promise.resolve()
and Promise.reject()
. For example, you may have a function that needs to return a promise, but which handles some cases synchronously: function readFilePromise(filename) { if (!filename) { return Promise.reject(new Error("Filename not specified")); } if (filename === 'index.html') { return Promise.resolve('<h1>Hello!</h1>'); } return new Promise((resolve, reject) => {/*...*/}) }
Promise.reject()
, however, it is recommended that you always pass an Error
object to this method.Promise.all() —
a convenient method for simultaneously executing an array of promises. For example, let's say we have a list of files that we want to read from the disk. Using the previously created readFilePromise
function, the solution to this problem may look like this: let filenames = ['index.html', 'blog.html', 'terms.html']; Promise.all(filenames.map(readFilePromise)) .then(files => { console.log('index:', files[0]); console.log('blog:', files[1]); console.log('terms:', files[2]); })
Promise.all
, this is an API, after a while, when you exceed the limit on the frequency of calls to it, it may well start to generate error 429 .Promise.al
l to perform such an operation (I would like to know why?), But the Array.reduce method can help us here: let itemIDs = [1, 2, 3, 4, 5]; itemIDs.reduce((promise, itemID) => { return promise.then(_ => api.deleteItem(itemID)); }, Promise.resolve());
api.deleteItem()
before making the next call. This code demonstrates a convenient way of processing the operation, which would otherwise have to be rewritten using then()
for each element identifier: Promise.resolve() .then(_ => api.deleteItem(1)) .then(_ => api.deleteItem(2)) .then(_ => api.deleteItem(3)) .then(_ => api.deleteItem(4)) .then(_ => api.deleteItem(5));
Promise.race
. As well as Promise.all
, it accepts an array of promises and executes them simultaneously, however, it is returned from it as soon as any of the promises is completed or rejected. The results of other promises are discarded. function timeout(ms) { return new Promise((resolve, reject) => { setTimeout(reject, ms); }) } Promise.race([readFilePromise('index.html'), timeout(1000)]) .then(data => console.log(data)) .catch(e => console.log("Timed out after 1 second"))
.catch()
block to the end of the chain, which will intercept errors that occur in any of the preceding .then()
blocks: Promise.resolve() .then(_ => api.getItem(1)) .then(item => { item.amount++; return api.updateItem(1, item); }) .catch(e => { console.log('failed to get or update item'); })
catch()
block is called here if either getItem
or updateItem
fails. But what if we don’t need joint error handling and need to handle errors occurring in getItem
separately? To do this, just insert another catch()
block immediately after the block with the getItem —
call getItem —
it can even return another promise: Promise.resolve() .then(_ => api.getItem(1)) .catch(e => api.createItem(1, {amount: 0})) .then(item => { item.amount++; return api.updateItem(1, item); }) .catch(e => { console.log('failed to update item'); })
getItem()
fails, we intervene and create a new item.then()
expression should be perceived as if it were inside a try
block. Both the return Promise.reject()
call return Promise.reject()
and the throw new Error()
call will execute the next catch()
block.catch()
blocks, so when it comes to error handling, you should not make assumptions about their source. For example, in the following code snippet, we can expect the catch()
block to be called only to handle errors that occurred during getItem
, but, as the example shows, it also responds to runtime errors that occur inside the then()
expression: api.getItem(1) .then(item => { delete item.owner; console.log(item.owner.name); }) .catch(e => { console.log(e); // Cannot read property 'name' of undefined })
function readFileAndMaybeLock(filename, createLockFile) { let promise = Promise.resolve(); if (createLockFile) { promise = promise.then(_ => writeFilePromise(filename + '.lock', '')) } return promise.then(_ => readFilePromise(filename)); }
promise
value, using the construction of the form promise = promise.then(/*...*/)
. Associated with this example is what we will discuss below in the “Multiple .then () call” section. api.getItem(1) .then(item => { item.amount++; api.updateItem(1, item) .then(update => { api.deleteItem(1) .then(deletion => { console.log('done!'); }) }) })
.then()
.return
in the promise chain. For example, can you find an error in this code? api.getItem(1) .then(item => { item.amount++; api.updateItem(1, item); }) .then(update => { return api.deleteItem(1); }) .then(deletion => { console.log('done!'); })
return
before api.updateItem
on line 4, and this particular block then()
resolved immediately. As a result, api.deleteItem()
is likely to be called before the api.updateItem()
call is completed.then()
can return either a value or a new Promise
object, but it can also return undefined
. Personally, if I were in charge of the JavaScript pre-API API, I would have provided for a run-time error if the .then()
block returned undefined
. However, this is not implemented in the language, so now we only need to be careful and perform an explicit return from any promise we create..then()
many times in the same promise, and callbacks will be called in the same order in which they are registered. However, I have never seen a real reason for doing so. Such actions can lead to incomprehensible effects when using return values of promises and when handling errors: let p = Promise.resolve('a'); p.then(_ => 'b'); p.then(result => { console.log(result) // 'a' }) let q = Promise.resolve('a'); q = q.then(_ => 'b'); q = q.then(result => { console.log(result) // 'b' })
p
on the next call to then()
, we will never see the return of 'b'
. Promis q
more predictable; we update it every time by calling then()
. let p = Promise.resolve(); p.then(_ => {throw new Error("whoops!")}) p.then(_ => { console.log('hello!'); // 'hello!' }) let q = Promise.resolve(); q = q.then(_ => {throw new Error("whoops!")}) q = q.then(_ => { console.log('hello'); // })
p
not updated, we end up in the second then()
..then()
call allows you to create several new independent promises from the original promise, however, I still have not managed to find a real use for this effect.then()
or catch() —
blocks catch() —
otherwise, the promis will absorb all of the following errors, processing them as part of the promise chain. Here is an example of wrapping a promise into a callback, which, at first glance, may seem quite suitable for practical use: function getThing(callback) { api.getItem(1) .then(item => callback(null, item)) .catch(e => callback(e)); } getThing(function(err, thing) { if (err) throw err; console.log(thing); })
catch()
block is present in the chain. This is because the callback()
is called both inside then()
and inside catch()
, which makes it part of the promise chain.setTimeout
, or process.nextTick
function in Node.js to exit the promise: function getThing(callback) { api.getItem(1) .then(item => setTimeout(_ => callback(null, item))) .catch(e => setTimeout(_ => callback(e))); } getThing(function(err, thing) { if (err) throw err; console.log(thing); })
try/catch
paradigm, but it does not support error handling in the called code by its calling construct, as it is done, for example, in Java. However, in JS it is common to use callbacks, the first parameter of which is the error object (this callback is also called “errback”). This forces the construct calling the method to at least consider the possibility of an error. Here is an example with the fs
library: fs.readFile('index.html', 'utf8', (err, data) => { if (err) throw err; console.log(data); })
(node:29916) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: whoops! (node:29916) DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
catch()
to the end of the promise chain.Source: https://habr.com/ru/post/339414/
All Articles