Implementing a backup using promises

this is a common template that we cascade through a list of data sources with the first success breaking the chain as follows:

var data = getData1(); if (!data) data = getData2(); if (!data) data = getData3(); 

et cetera. if the getDataN () functions are asynchronous, this leads us to a "backward hell":

 var data; getData1(function() { getData2(function () { getData3(function () { alert('not found'); }) }) }); 

where implementations might look something like this:

 function getData1(callback) { $.ajax({ url: '/my/url/1/', success: function(ret) { data = ret }, error: callback }); } 

... with promises I would like to write something like this:

 $.when(getData1()) .then(function (x) { data = x; }) .fail(function () { return getData2(); }) .then(function (x) { data = x; }) .fail(function () { return getData3(); }) .then(function (x) { data = x; }); 

where the second .then actually refers to the return value of the first .fail , which in itself is a promise, and which I understood was bound as an input to the next step of the chain.

it’s clear that I am wrong, but what is the correct way to write this?

+6
source share
1 answer

In most of the libs of the same name, you can link .fail() or .catch() , as in @ mido22's answer, but jQuery .fail() does not "handle" the error as such. It is guaranteed that an input promise (with an unchanged state) will always be transmitted, which would not allow to “break” the cascade if / when success occurs.

The only jQuery Promise method that can return a promise with a different state (or a different value / reason) is .then() .

Therefore, you can write a chain that continues in error by specifying the next step as an error handler at each stage.

 function getDataUntilAsyncSuccess() { return $.Deferred().reject() .then(null, getData1) .then(null, getData2) .then(null, getData3); } //The nulls ensure that success at any stage will pass straight through to the first non-null success handler. getDataUntilAsyncSuccess().then(function (x) { //"success" data is available here as `x` }, function (err) { console.log('not found'); }); 

But in practice, you can often create an array of functions or data objects, which in turn are called using the Array .reduce() method.

For instance:

 var fns = [ getData1, getData2, getData3, getData4, getData5 ]; function getDataUntilAsyncSuccess(data) { return data.reduce(function(promise, fn) { return promise.then(null, fn); }, $.Deferred().reject());// a rejected promise to get the chain started } getDataUntilAsyncSuccess(fns).then(function (x) { //"success" data is available here as `x` }, function (err) { console.log('not found'); }); 

Or perhaps this is the best solution here:

 var urls = [ '/path/1/', '/path/2/', '/path/3/', '/path/4/', '/path/5/' ]; function getDataUntilAsyncSuccess(data) { return data.reduce(function(promise, url) { return promise.then(null, function() { return getData(url);// call a generalised `getData()` function that accepts a URL. }); }, $.Deferred().reject());// a rejected promise to get the chain started } getDataUntilAsyncSuccess(urls).then(function (x) { //"success" data is available here as `x` }, function (err) { console.log('not found'); }); 
+11
source

Source: https://habr.com/ru/post/988703/


All Articles