Reputation: 16764
I have steps array like:
var stepsToDo = [step1, step2, step3,...]
every step does ajax call:
function step1() { return $.ajax({ ... }); }
function step2() { return $.ajax({ ... }); }
The sample ajax call for step 1
(step 2
and others are similar):
return $.ajax({
url: "test.php",
type: "POST",
dataType: "json",
data: {"step-1": ""},
beforeSend: function () {
writeResult("Processing-> test 1");
},
success: function (result) {
if (result) {
if (result instanceof Array) {
writeResult("Found: " + result.length + " items");
arr = result;
} else {
writeResult(result);
arr = [];
}
}
}
});
function writeResult(str) { console.log(str); }
I want to execute sequentially (defined in stepsToDo).
I tried like:
stepsToDo.reduce(
(promise, method) => {
return promise.then(method);
},
Promise.resolve()
);
Nothing happens, no print in console happens.
Why ?
Upvotes: 0
Views: 401
Reputation: 6058
There most be something missing from the original question or the implementation. If all the step functions return the$.ajax
promise then the Array.reduce
pattern should work.
Below is a working prove of concept using a step()
function to simulate the asynchronous code execution and using the exact same Array.reduce
pattern:
// Step generator
function step(number) {
return function() {
return $.Deferred(function(def) {
setTimeout(function() {
console.log('Running Step ', number);
def.resolve();
}, Math.floor(Math.random() * Math.floor(250)));
}).promise();
}
}
// List of steps
var stepsToDo = [step(1), step(2), step(3), step(4), step(5)];
// Consume steps one at a time
stepsToDo.reduce(
(promise, method) => {
return promise.then(method);
},
Promise.resolve()
).then(() => console.log('All Done!'));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Upvotes: 0
Reputation: 39270
I'm not sure what's wrong with the other answers but $.get (or ajax or post) will return a promise like.
So your step methods can look like this:
var stepOne = () => {
return $.ajax({//just return the promise like
url: "test.php",
type: "POST",
dataType: "json",
data: {"step-1": ""}
});
}
You can then reduce it to one promise that resolves with the results of the three steps:
[stepOne,stepTwo,stepThree].reduce(
(p,fn)=>
p.then(
results=>fn().then(result=>results.concat([result]))
),
$.Deferred().resolve([])
)
.then(
results=>console.log("got results:",results),
err=>console.warn("Something went wrong:",err)
);
You see I don't pass Promise.resolve
as second argument to reduce but $.Deferred().resolve([])
, this is jQuery promise like value. You can now support browsers that don't have native promises without the need to polyfill.
Although if you need to support those I'd recomment not using the arrow function syntax and use function(something){return something}
instead of something=>something
Upvotes: 0
Reputation: 7916
For the reducer function you initially created...
stepsToDo.reduce((promise, method) => {
return promise.then(_ => new Promise(method));
}, Promise.resolve());
...your step functions should resolve
or reject
the promises created in your reducer - therefore should implement the promise executor signature, I.E:
type executor = (resolve: (value?: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void;
For example:
function step1(resolve, reject) {
$
.ajax({ /* ... */ })
.done(resolve)
.fail(reject);
}
EDIT
As @Bergi states in the comments, this is an anti-pattern. Instead you could move the Promise construction into your step functions. A basic example (the jQuery ajax API still has to be converted to promises)
function step1() {
return new Promise((resolve, reject) => {
$.ajax({ /* ... */ }).done(resolve).fail(reject);
});
}
If all your steps return you promises your reducer can get much simpler:
stepsToDo.reduce((promise, method) => promise.then(method), Promise.resolve());
You could in this case even just return the result of $.ajax
, since jQuery promises do implement a then method:
function step1() {
return $.ajax({ /* ... */ });
}
If a native promise is required for error handling, i.e. Promise.catch
, you can explicitly cast the jQuery promise to a native promise with Promise.resolve
:
function step1() {
return Promise.resolve($.ajax({ /* ... */ }));
}
Upvotes: 0
Reputation: 664548
Drop the new Promise
. Your steps are functions that return promises, not promise executors - they never would call a resolve
callback passed into them.
Upvotes: 1