You have a prototype object Foo with two async method calls, bar and baz.
var bob = new Foo()
Foo.prototype.bar = function land(callback) {
setTimeout(function() {
callback()
console.log('bar');
}, 3000);
};
Foo.prototype.baz = function land(callback) {
setTimeout(function() {
callback()
console.log('baz');
}, 3000);
};
We want to do bob.bar().baz() and have it log "bar" and "baz" sequentially.
If you cannot modify the method calls (including passing in your callback function), how can you pass a default callback into these method calls?
Some ideas:
Wrap "bob" with decorator (still fuzzy on how to implement, could use a small example)
Modify constructor to assign default callback if none assigned (have not considered if this is possible or not)
Use a generator wrapper that will continue to call next method until none are left?
The more recommended way instead is to use promises as this is the community-wide practice to do async work.
We want to do bob.bar().baz() and have it log "bar" and "baz"
sequentially.
Why would you want to do that just to achieve this bob.bar().baz() "syntax"? You could do it pretty neatly using the Promise API w/o additional efforts to make that syntax work that indeed increases code complexity reducing the actual readability.
So, you might want to consider using the promise-based approach like this. It offers much flexibility than what you would have achieved with your approach:
Foo.prototype.bar = function () {
return new Promise(function (resolve) {
setTimeout(function () {
resolve()
console.log('bar');
}, 3000);
};
};
Foo.prototype.baz = function () {
return new Promise(function (resolve) {
setTimeout(function () {
resolve()
console.log('baz');
}, 3000);
};
};
Now you'd do this to run them sequentially one after another:
var bob = new Foo();
bob.bar().then(function() {
return bob.baz();
});
// If you're using ES2015+ you could even do:
bob.bar().then(() => bob.baz());
If you need to chain more functions you could simply do it:
bob.bar()
.then(() => bob.baz())
.then(() => bob.anotherBaz())
.then(() => bob.somethingElse());
Anyway, if you're not used to using promises you might want to read this
Warning this isn't quite right yet. Ideally we'd subclass Promise and have proper then/catch functionality but there are some caveats with subclassing bluebird Promise. The idea is to store an internal array of promise generating functions, then when a Promise is waited on (then/await) serially wait on those promises.
const Promise = require('bluebird');
class Foo {
constructor() {
this.queue = [];
}
// promise generating function simply returns called pGen
pFunc(i,pGen) {
return pGen();
}
bar() {
const _bar = () => {
return new Promise( (resolve,reject) => {
setTimeout( () => {
console.log('bar',Date.now());
resolve();
},Math.random()*1000);
})
}
this.queue.push(_bar);
return this;
}
baz() {
const _baz = () => {
return new Promise( (resolve,reject) => {
setTimeout( () => {
console.log('baz',Date.now());
resolve();
},Math.random()*1000);
})
}
this.queue.push(_baz);
return this;
}
then(func) {
return Promise.reduce(this.queue, this.pFunc, 0).then(func);
}
}
const foo = new Foo();
foo.bar().baz().then( () => {
console.log('done')
})
result:
messel#messels-MBP:~/Desktop/Dropbox/code/js/async-chain$ node index.js
bar 1492082650917
baz 1492082651511
done
If you want to avoid callback hell and keep your sanity ES6 promises are the most appropriate approach for the sake of functional programming. You just chain up your sequential asynchronous tasks in the asynchronous timeline just like working in a synchronous timeline.
In this particular case you just need to promisify your asynchronous functions. Assume that your asynch functions takes a data and a callback like asynch(data,myCallback). Let us assume that the callback is error first type.
Such as;
var myCallback = (error,result) => error ? doErrorAction(error)
: doNormalAction(result)
When your asynch function is promisified, you will actually be returned a function which takes your data and returns a promise. You are expected to apply myCallback at the then stage. The return value of myCallback will then be passed to the next stage at where you can invoke another asynch function supplied with the return value of myCallback and this goes on and on as long as you need. So let's see how we shall implement this abstract to your workflow.
function Foo(){}
function promisify(fun){
return (data) => new Promise((resolve,reject) => fun(data, (err,res) => err ? reject(err) : resolve(res)));
}
function myCallback(val) {
console.log("hey..! i've got this:",val);
return val;
}
var bob = new Foo();
Foo.prototype.bar = function land(value, callback) {
setTimeout(function() {
callback(false,value*2); // no error returned but value doubled and supplied to callback
console.log('bar');
}, 1000);
};
Foo.prototype.baz = function land(value, callback) {
setTimeout(function() {
callback(false,value*2); // no error returned but value doubled and supplied to callback
console.log('baz');
}, 1000);
};
Foo.prototype.bar = promisify(Foo.prototype.bar);
Foo.prototype.baz = promisify(Foo.prototype.baz);
bob.bar(1)
.then(myCallback)
.then(bob.baz)
.then(myCallback)
Related
I am looking at an implementation of a Promise class (at bottom). The constructor takes a function, f.
So if I have a regular function I want to pass in:
function myFunc() {
console.log("My Func");
}
If a Promise looks like this :
let promise = new Promise(resolve => {
myFunc();
}
then I am confused as to what f represents now though in the constructor of the class below. It appears as though resolve is the argument, not a function, f.
*Promise Class
class Promise {
constructor(f) {
this.onResolves = [];
this.status = "pending";
const resolve = value => {
debugger
this.status = "resolved";
this.value = value;
this.callOnResolves();
};
f(resolve);
}
callOnResolves() {
if (this.status === "resolved") {
this.onResolves.forEach(onResolve => onResolve(this.value));
this.onResolves.length = 0;
}
}
then(onResolve) {
return new Promise(resolve => {
this.onResolves.push(value => resolve(onResolve(value)));
this.callOnResolves();
});
}
}
f is the function that you pass to the promise's constructor. It takes a resolve parameter, which should eventually be called with the result of the asynchronous work.
Typically, your anonymous function would make use of an api that supports callbacks instead of promises, and in your callback to that api you would call resolve to let the Promise know that your asynchronous work is complete.
This is actually how you could implement an asynchronous sleep method using setTimeout.
function sleep(milliseconds) {
return new Promise(resolve => {
setTimeout(resolve, milliseconds);
});
}
sleep(1000).then(() => console.log(new Date()));
I read that async functions marked by the async keyword implicitly return a promise:
async function getVal(){
return await doSomethingAync();
}
var ret = getVal();
console.log(ret);
but that is not coherent...assuming doSomethingAsync() returns a promise, and the await keyword will return the value from the promise, not the promise itsef, then my getVal function should return that value, not an implicit promise.
So what exactly is the case? Do functions marked by the async keyword implicitly return promises or do we control what they return?
Perhaps if we don't explicitly return something, then they implicitly return a promise...?
To be more clear, there is a difference between the above and
function doSomethingAync(charlie) {
return new Promise(function (resolve) {
setTimeout(function () {
resolve(charlie || 'yikes');
}, 100);
})
}
async function getVal(){
var val = await doSomethingAync(); // val is not a promise
console.log(val); // logs 'yikes' or whatever
return val; // but this returns a promise
}
var ret = getVal();
console.log(ret); //logs a promise
In my synopsis the behavior is indeed inconsistent with traditional return statements. It appears that when you explicitly return a non-promise value from an async function, it will force wrap it in a promise.
I don't have a big problem with it, but it does defy normal JS.
The return value will always be a promise. If you don't explicitly return a promise, the value you return will automatically be wrapped in a promise.
async function increment(num) {
return num + 1;
}
// Even though you returned a number, the value is
// automatically wrapped in a promise, so we call
// `then` on it to access the returned value.
//
// Logs: 4
increment(3).then(num => console.log(num));
Same thing even if there's no return! (Promise { undefined } is returned)
async function increment(num) {}
Same thing even if there's an await.
function defer(callback) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve(callback());
}, 1000);
});
}
async function incrementTwice(num) {
const numPlus1 = await defer(() => num + 1);
return numPlus1 + 1;
}
// Logs: 5
incrementTwice(3).then(num => console.log(num));
Promises auto-unwrap, so if you do return a promise for a value from within an async function, you will receive a promise for the value (not a promise for a promise for the value).
function defer(callback) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve(callback());
}, 1000);
});
}
async function increment(num) {
// It doesn't matter whether you put an `await` here.
return defer(() => num + 1);
}
// Logs: 4
increment(3).then(num => console.log(num));
In my synopsis the behavior is indeed inconsistent with traditional
return statements. It appears that when you explicitly return a
non-promise value from an async function, it will force wrap it in a
promise. I don't have a big problem with it, but it does defy normal
JS.
ES6 has functions which don't return exactly the same value as the return. These functions are called generators.
function* foo() {
return 'test';
}
// Logs an object.
console.log(foo());
// Logs 'test'.
console.log(foo().next().value);
Yes, an async function will always return a promise.
According to the tc39 spec, an async function desugars to a generator which yields Promises.
Specifically:
async function <name>?<argumentlist><body>
Desugars to:
function <name>?<argumentlist>{ return spawn(function*() <body>, this); }
Where spawn "is a call to the following algorithm":
function spawn(genF, self) {
return new Promise(function(resolve, reject) {
var gen = genF.call(self);
function step(nextF) {
var next;
try {
next = nextF();
} catch(e) {
// finished with failure, reject the promise
reject(e);
return;
}
if(next.done) {
// finished with success, resolve the promise
resolve(next.value);
return;
}
// not finished, chain off the yielded promise and `step` again
Promise.resolve(next.value).then(function(v) {
step(function() { return gen.next(v); });
}, function(e) {
step(function() { return gen.throw(e); });
});
}
step(function() { return gen.next(undefined); });
});
}
Your question is: If I create an async function should it return a promise or not? Answer: just do whatever you want and Javascript will fix it for you.
Suppose doSomethingAsync is a function that returns a promise. Then
async function getVal(){
return await doSomethingAsync();
}
is exactly the same as
async function getVal(){
return doSomethingAsync();
}
You probably are thinking "WTF, how can these be the same?" and you are right. The async will magically wrap a value with a Promise if necessary.
Even stranger, the doSomethingAsync can be written to sometimes return a promise and sometimes NOT return a promise. Still both functions are exactly the same, because the await is also magic. It will unwrap a Promise if necessary but it will have no effect on things that are not Promises.
Just add await before your function when you call it :
var ret = await getVal();
console.log(ret);
async doesn't return the promise, the await keyword awaits the resolution of the promise. async is an enhanced generator function and await works a bit like yield
I think the syntax (I am not 100% sure) is
async function* getVal() {...}
ES2016 generator functions work a bit like this. I have made a database handler based in top of tedious which you program like this
db.exec(function*(connection) {
if (params.passwd1 === '') {
let sql = 'UPDATE People SET UserName = #username WHERE ClinicianID = #clinicianid';
let request = connection.request(sql);
request.addParameter('username',db.TYPES.VarChar,params.username);
request.addParameter('clinicianid',db.TYPES.Int,uid);
yield connection.execSql();
} else {
if (!/^\S{4,}$/.test(params.passwd1)) {
response.end(JSON.stringify(
{status: false, passwd1: false,passwd2: true}
));
return;
}
let request = connection.request('SetPassword');
request.addParameter('userID',db.TYPES.Int,uid);
request.addParameter('username',db.TYPES.NVarChar,params.username);
request.addParameter('password',db.TYPES.VarChar,params.passwd1);
yield connection.callProcedure();
}
response.end(JSON.stringify({status: true}));
}).catch(err => {
logger('database',err.message);
response.end(JSON.stringify({status: false,passwd1: false,passwd2: false}));
});
Notice how I just program it like normal synchronous particularly at
yield connection.execSql and at yield connection.callProcedure
The db.exec function is a fairly typical Promise based generator
exec(generator) {
var self = this;
var it;
return new Promise((accept,reject) => {
var myConnection;
var onResult = lastPromiseResult => {
var obj = it.next(lastPromiseResult);
if (!obj.done) {
obj.value.then(onResult,reject);
} else {
if (myConnection) {
myConnection.release();
}
accept(obj.value);
}
};
self._connection().then(connection => {
myConnection = connection;
it = generator(connection); //This passes it into the generator
onResult(); //starts the generator
}).catch(error => {
reject(error);
});
});
}
I am trying to understand the difference between the 3 options:
.then(myCallback)
.then(myCallback())
.then(() => { myCallback() })
The myCallback function returns nothing and is used only for its side effects, so I don't need it to pass anything back to the promise chain. What I don't understand is why in my code only the second option triggers the function, when it seems like all the 3 should.
UPDATE: Here is the code with the bare essentials
serverCall(url, data) // fetch function
.then((response) => response.json())
.then(myCallback) // not running, only when () added
.catch((error) => { console.log(error) })
const myCallback = () => {
anotherServerCall(...).then(...)
}
UPDATE 2
After some digging I see that having .then((response) => response.json()) as the first then response is what is preventing .then(myCallback) to execute. Still no idea why...
All three should trigger the function, but the order differs between the approaches. If you're sometimes seeing evidence that the serverCall or myCallback aren't being invoked, then that has something to do with the particulars of those functions, not the ways you are calling them.
To demonstrate, consider two proxies for serverCall and myCallback that we know will always work. Let's apply each idea in your question to those:
This is the "normal" way to use then. Pass it a function that is invoked after the promise to which it is attached...
function serverCall() {
console.log('began server call');
return new Promise(function(resolve) {
setTimeout(() => {
console.log('completed server call');
resolve();
}, 2);
});
}
function myCallback() {
console.log('callback called');
}
serverCall().then(myCallback).then(() => console.log('done'));
// produces:
// began server call
// completed server call
// callback called
// done
Your first and third ideas are almost the same. Pass then a function which is invoked after the promise. In your third idea, the function isn't the callback, it's a function that calls the callback. One more stack frame, but the exact same effect...
function serverCall() {
console.log('began server call');
return new Promise(function(resolve) {
setTimeout(() => {
console.log('completed server call');
resolve();
}, 2);
});
}
function myCallback() {
console.log('callback called');
}
serverCall().then(() => { myCallback() }).then(() => console.log('done'))
// produces:
// began server call
// completed server call
// callback called
// done
Your second idea, as a commenter points out, invokes the function and passes it's result to then. The chaining of the then runs synchronously after starting the promise, so the results appear reordered: myCallback runs before the promise completes...
function serverCall() {
console.log('began server call');
return new Promise(function(resolve) {
setTimeout(() => {
console.log('completed server call');
resolve();
}, 2);
});
}
function myCallback() {
console.log('callback called');
}
serverCall().then(myCallback()).then(() => console.log('done'))
// produces:
// began server call
// callback called <------ CHANGED!!!
// completed server call
// done
The only way I've found to duplicate your problem is if the callback returns a function. Options 1 and 3 don't do anything because the returned function isn't being called. Option 2 is called and is successful.
function fetch() {
return new Promise((resolve, reject) => {
resolve();
});
}
function myCallBack() {
return function () {
console.log('Success');
};
}
fetch().then(myCallBack); // nothing
fetch().then(myCallBack()); // Success
fetch().then(() => myCallBack()); // nothing
Your use cases are different because
The first one then call, you are passing a callback to be invoked when then it’s executed, passing a naming function will appear in the stack trace if some error occurred
The second one, execute the callback passed before being passed to the then function and the result of that execution callback will be passing to the then function, so imagine this.
function myCallback(){
return function theRealCallbackPassed(thenResponse){
// do something with then response
}
}
The last one, will define an anonymous arrow function , so what is the diff between
then( () => {} )
And
then( function(){} )
The different is that using arrow function () => {} you have a short syntax and you are binded the function contexto to the current context where the arrow function it’s declared.
While the function() {} syntax is not shorter and it is not auto bindable.
Hope it can help you.
I was just reading this fantastic article «Generators» and it clearly highlights this function, which is a helper function for handling generator functions:
function async(makeGenerator){
return function () {
var generator = makeGenerator.apply(this, arguments);
function handle(result){
// result => { done: [Boolean], value: [Object] }
if (result.done) return Promise.resolve(result.value);
return Promise.resolve(result.value).then(function (res){
return handle(generator.next(res));
}, function (err){
return handle(generator.throw(err));
});
}
try {
return handle(generator.next());
} catch (ex) {
return Promise.reject(ex);
}
}
}
which I hypothesize is more or less the way the async keyword is implemented with async/await. So the question is, if that is the case, then what the heck is the difference between the await keyword and the yield keyword? Does await always turn something into a promise, whereas yield makes no such guarantee? That is my best guess!
You can also see how async/await is similar to yield with generators in this article where he describes the 'spawn' function ES7 async functions.
Well, it turns out that there is a very close relationship between async/await and generators. And I believe async/await will always be built on generators. If you look at the way Babel transpiles async/await:
Babel takes this:
this.it('is a test', async function () {
const foo = await 3;
const bar = await new Promise(resolve => resolve('7'));
const baz = bar * foo;
console.log(baz);
});
and turns it into this
function _asyncToGenerator(fn) {
return function () {
var gen = fn.apply(this, arguments);
return new Promise(function (resolve, reject) {
function step(key, arg) {
try {
var info = gen[key](arg);
var value = info.value;
} catch (error) {
reject(error);
return;
}
if (info.done) {
resolve(value);
} else {
return Promise.resolve(value).then(function (value) {
return step("next", value);
}, function (err) {
return step("throw", err);
});
}
}
return step("next");
});
};
}
this.it('is a test', _asyncToGenerator(function* () { // << now it's a generator
const foo = yield 3; // <<< now it's yield, not await
const bar = yield new Promise(resolve => resolve(7));
const baz = bar * foo;
console.log(baz);
}));
you do the math.
This makes it look like the async keyword is just that wrapper function, but if that's the case then await just gets turned into yield, there will probably be a bit more to the picture later on when they become native.
You can see more of an explanation for this here:
https://www.promisejs.org/generators/
yield can be considered to be the building block of await. yield takes the value it's given and passes it to the caller. The caller can then do whatever it wishes with that value (1). Later the caller may give a value back to the generator (via generator.next()) which becomes the result of the yield expression (2), or an error that will appear to be thrown by the yield expression (3).
async-await can be considered to use yield. At (1) the caller (i.e. the async-await driver - similar to the function you posted) will wrap the value in a promise using a similar algorithm to new Promise(r => r(value) (note, not Promise.resolve, but that's not a big deal). It then waits for the promise to resolve. If it fulfills, it passes the fulfilled value back at (2). If it rejects, it throws the rejection reason as an error at (3).
So the utility of async-await is this machinery that uses yield to unwrap the yielded value as a promise and pass its resolved value back, repeating until the function returns its final value.
what the heck is the difference between the await keyword and the yield keyword?
The await keyword is only to be used in async functions, while the yield keyword is only to be used in generator function*s. And those are obviously different as well - the one returns promises, the other returns generators.
Does await always turn something into a promise, whereas yield makes no such guarantee?
Yes, await will call Promise.resolve on the awaited value.
yield just yields the value outside of the generator.
tl;dr
Use async/await 99% of the time over generators. Why?
async/await directly replaces the most common workflow of promise chains allowing code to be declared as if it was synchronous, dramatically simplifying it.
Generators abstract the use case where you would call a series of async-operations that depend on each other and eventually will be in a "done" state. The most simple example would be paging through results that eventually return the last set but you would only call a page as needed, not immediately in succession.
async/await is actually an abstraction built on top of generators to make working with promises easier.
See very in-depth Explanation of Async/Await vs. Generators
Try this test programs which I used to understand await/async with promises.
Program #1: without promises it doesn't run in sequence
function functionA() {
console.log('functionA called');
setTimeout(function() {
console.log('functionA timeout called');
return 10;
}, 15000);
}
function functionB(valueA) {
console.log('functionB called');
setTimeout(function() {
console.log('functionB timeout called = ' + valueA);
return 20 + valueA;
}, 10000);
}
function functionC(valueA, valueB) {
console.log('functionC called');
setTimeout(function() {
console.log('functionC timeout called = ' + valueA);
return valueA + valueB;
}, 10000);
}
async function executeAsyncTask() {
const valueA = await functionA();
const valueB = await functionB(valueA);
return functionC(valueA, valueB);
}
console.log('program started');
executeAsyncTask().then(function(response) {
console.log('response called = ' + response);
});
console.log('program ended');
Program #2: with promises
function functionA() {
return new Promise((resolve, reject) => {
console.log('functionA called');
setTimeout(function() {
console.log('functionA timeout called');
// return 10;
return resolve(10);
}, 15000);
});
}
function functionB(valueA) {
return new Promise((resolve, reject) => {
console.log('functionB called');
setTimeout(function() {
console.log('functionB timeout called = ' + valueA);
return resolve(20 + valueA);
}, 10000);
});
}
function functionC(valueA, valueB) {
return new Promise((resolve, reject) => {
console.log('functionC called');
setTimeout(function() {
console.log('functionC timeout called = ' + valueA);
return resolve(valueA + valueB);
}, 10000);
});
}
async function executeAsyncTask() {
const valueA = await functionA();
const valueB = await functionB(valueA);
return functionC(valueA, valueB);
}
console.log('program started');
executeAsyncTask().then(function(response) {
console.log('response called = ' + response);
});
console.log('program ended');
In many ways, generators are a superset of async/await. Right now async/await has cleaner stack traces than co, the most popular async/await-like generator based lib. You can implement your own flavor of async/await using generators and add new features, like built-in support for yield on non-promises or building it on RxJS observables.
So, in short, generators give you more flexibility and generator-based libs generally have more features. But async/await is a core part of the language, it's standardized and won't change under you, and you don't need a library to use it. I have a blog post with more details on the difference between async/await and generators.
The yield+gen.next()-as-a-language-feature can be used to describe (or implement) the underlying control-flow that await-async has abstracted away.
As other answers suggest, await-as-a-language-feature is (or can be thought of) an implementation on top of yield.
Here is a more intutive understanding for that:
Say we have 42 awaits in an async function, await A -> await B -> ...
Deep down it is equivalent to having yield A -> tries resolve this as a Promise [1]
-> if resolvable, we yield B, and repeat [1] for B
-> if not resolveable, we throw
And so we end up with 42 yields in a generator. And in our controller we simply keep doing gen.next() until it is completed or gets rejected. (ie this is the same as using await on an async function that contains 42 await.)
This is why lib like redux-saga utilizes generator to then pipe the promises to the saga middleware to be resolved all at one place; thus decoupling the Promises constructions from their evaluations, thus sharing close resemblance to the Free Monad.
The idea is to recursively chain then() calls to replicate the behavior of await which allows one to invoke async routines in a synchronous fashion. A generator function is used to yield back control (and each value) from the callee to the caller, which happens to be the _asyncToGenerator() wrapper function.
As mentioned above, this is the trick that Babel uses to create polyfills. I slightly edited the code to make it more readable and added comments.
(async function () {
const foo = await 3;
const bar = await new Promise((resolve) => resolve(7));
const baz = bar * foo;
console.log(baz);
})();
function _asyncToGenerator(fn) {
return function () {
let gen = fn(); // Start the execution of the generator function and store the generator object.
return new Promise(function (resolve, reject) {
function step(func, arg) {
try {
let item = gen[func](arg); // Retrieve the function object from the property name and invoke it. Similar to eval(`gen.${func}(arg)`) but safer. If the next() method is called on the generator object, the item value by the generator function is saved and the generator resumes execution. The value passed as an argument is assigned as a result of a yield expression.
if (item.done) {
resolve(item.value);
return; // The executor return value is ignored, but we need to stop the recursion here.
}
// The trick is that Promise.resolve() returns a promise object that is resolved with the value given as an argument. If that value is a promise object itself, then it's simply returned as is.
return Promise.resolve(item.value).then(
(v) => step("next", v),
(e) => step("throw", e)
);
} catch (e) {
reject(e);
return;
}
}
return step("next");
});
};
}
_asyncToGenerator(function* () { // <<< Now it's a generator function.
const foo = yield 3; // <<< Now it's yield, not await.
const bar = yield new Promise((resolve, reject) => resolve(7)); // <<< Each item is converted to a thenable object and recursively enclosed into chained then() calls.
const baz = bar * foo;
console.log(baz);
})();
I have a question about promises.
I am using Bluebird Promise library and build a small async library with it.
I am trying to waterfall promises with the use of a function.
Say I use promises like so:
Promise.resolve()
.then(function () {
console.log("called 1")
return 1;
}).then(function () {
return new Promise (function (res, rej) {
setTimeout(function () {
console.log("called 2")
res(2);
}, 1500);
});
}).then(function () {
console.log("called 3")
return 3;
});
This does in fact wait in a loop and return 1,2,3 in order.
How do I wrap it into a function so that I can do something like this:
a();b();c();, or a().b().c(); where a() puts something onto a chain, b() puts something onto a chain, and c() puts something onto a chain in order.
Since then() returns a new promise, it can all go out of order, so something like
does not work:
var promise = Promise.resolve();
function a () {
promise.then(function () {
// do sync/async
});
}
function b () {
promise.then(function () {
//do sync/async
});
}
function c ...
Thank you for your time :]
I'm not sure what the goal is here. Do you want to have an arbitrary number of things run in sequence where the sequence is known in advance? Or is this a case where the sequence is discovered as you go? The NodeJS streams interface is a lot better for processing an unknown number of things sequentially (#tadman)
Sequence is discoverable, goal is to have ability to call a().b().c() or b().a().d(). Async library on a client-side.
Update: If I do as #zerkms says it does not work as expected. My bad, should work ok, but with lack of context/code did not give me enough info to expand on. Still thank you for your answer, as it gave me more food for thought.
Update: See my answer
You could use a scoped prototype and just add those methods there
Promise.prototype.a = function() {
return this.then(function() {
console.log("called 1")
return 1;
});
};
Promise.prototype.b = function() {
return this.delay(1500).then(function() {
console.log("called 2")
return 1;
});
};
Promise.prototype.c = function() {
return this.then(function() {
console.log("called 3")
return 3;
});
};
I use this to create neat DSLs e.g. with git:
https://gist.github.com/petkaantonov/6a73bd1a35d471ddc586
Thanks to #tadman I came up with this so far, seems to work as I expect it to.
The problem was that I did not update the promise before calling then on it, and it was branching instead of calling it in sequence.
And this is what I wanted - to turn an object that has both sync/async into async to allow chaining. Petka (#Esailija) also shows great example of building DSLs above (semvar version bumping & git pushing) by extending bluebird library, but for my purposes this is enough.
var Sample = function () {
this.promise = Promise.resolve();
};
Sample.prototype.a = function () {
this.then(function () {
console.log("1");
});
return this;
};
Sample.prototype.b = function () {
this.then(function () {
return new Promise(function (res, rej) {
setTimeout(function() {
console.log("2");
res();
}, 500);
});
});
return this;
};
Sample.prototype.c = function () {
this.then(function () {
console.log("3");
})
return this;
};
Sample.prototype.chainPromise = function (func) {
this.promise = this.promise.then(func);
};
var s = new Sample();
s.a().b().c();
or even then instead of chainPromise?
Sample.prototype.then = function (func) {
this.promise = this.promise.then(func);
return this.promise;
};