When to make a function deferred using Promises

2019-01-12 11:03发布

I'm using the Q node library for Promises, question I think can apply to the Bluebird lib as well.

Context

I have a few function calls to make to both my own custom functions and node.js fs style async functions.

if I'm making a call to a function like this:

sync function

do_something = function (input) {
  // assign variables, do other sync stuff 
}

and need the above to take place before this function:

sync function

do_something_else = function (input) {
  // assign variable, do other sync stuff
}

and then need to call a native node function similar to:

async function

writeData = function (obj, callback) {
  var deferred = Q.defer();
  fs.writeFile(obj.file, obj.datas, function (err, result) {
    if (err) deferred.reject(err);
    else deferred.resolve('write data completed');
  });
  return deferred.promise.nodeify(callback);
}

and finally need the above to take place before this function:

sync function

do_something_last = function (input) {
  // assign variable, do other sync stuff
}

Question

Is the 'right' thing to do here, to make all my functions 'deferred' or promise aware so I can make sure that they are called in sequence or in the correct order? like so:

do_something(variable)
.then(do_something_else)
.then(writeData)
.then(do_something_last)
.done();

or should I just do this instead and keep the ordering (sequencing)? Like so:

var variable1 = 'test1.txt'
var variable2 = 'test2.txt'
var return_value = do_something(variable1);
var return_another_value = do_something_else(return_value); <--sync
writeData(return_another_value); <-- async function
var final_value = do_something_last(variable2);  <-- sync function
// could potentially have async calls again after a sync call
writeDataAgain(return_another_value); <-- async function

Clarifications

What I thought was since some of these sync functions are going to need to be fired after the async, I needed to make them Promise aware in order to keep the sequence straight, like so:

sync functions made promise aware

do_something = function (input) {
  var deferred = Q.defer();
  // assign variables, do other sync stuff 
  deferred.resolve(_output_result_);
  return deferred.promise;
}
do_something_else = function (input) {
  var deferred = Q.defer();
  // assign variables, do other sync stuff 
  deferred.resolve(_output_result_);
  return deferred.promise;
}
do_something_last = function (input) {
  var deferred = Q.defer();
  // assign variables, do other sync stuff 
  deferred.resolve('completed workflow');
  return deferred.promise;
}

this would allow me to do this:

do_something(variable)
.then(do_something_else) <-- these need to execute before writeData
.then(writeData) <-- a async node fs call to writeFile
.then(do_something_last) <-- I need this to happen after the writeDate
.done();

After the feedback i've read, i guess what it seems like i'm really asking is:

How do I create a function workflow, mixing non-promise sync and promise-aware async function calls, all the while keeping the ordering (or sequencing) of execution?

2条回答
爷、活的狠高调
2楼-- · 2019-01-12 11:17

just do this instead and keep the ordering like so:

writeData(return_another_value);
var final_value = do_something_last(variable2);

Well, that simply won't work, as do_something_last is not called after the writeData(…) promise is resolved. It'll just start right after the promise is created and returned. So if you care about that particular order and want to wait until the data is written, then you need to use then with a callback:

var final_promise = writeData(return_another_value).then(function(writeResult) {
    return do_something_last(variable2);
});

The general rules are:

  • make synchronous functions synchronous - no need for promises
  • make all asynchronous functions always return a promise
  • use deferreds only at the lowest possible level for promisification

You can just place synchronous functions in a then chain, non-promise return values (or even thrown exceptions) work fine in them. So while you can write your sequence like

Q('test1.txt')
.then(do_something)
.then(do_something_else)
.then(writeData)
.then(do_something_last.bind(null, 'test2.txt'))
.done();

it looks rather odd. If you don't plan to make the do_somethings asynchronous in the near future for some reason, it's often simpler to write and read

writeData(do_something_else(do_something('test1.txt'))).then(function() {
    return do_something_last('test2.txt');
}).done();

Admittedly, it's sometimes more appealing to write

somePromise()
.then(doSomethingSynchronous)
.then(doSomethingAsynchronous)

than

somePromise
.then(function(res) { return doSomethingAsynchronous(doSomethingSynchronous(res)); })

even though they are functionally identical. Choose the style that you like better and that is more consistent.

查看更多
ら.Afraid
3楼-- · 2019-01-12 11:25

If all you care about is whether your functions go in sequence or not, then do this:

do_something(variable)
.then(do_something_else)
.then(writeData)
.then(do_something_last)
.done();

You'd only assign promises to variables when you're going to be passing those variables around (e.g. to other services), or using them to create different promise chains.

e.g

var promise = do_something('123')

// two different promise chains

var task1 = promise.then(function(res){
  // logic
})

var task2 = promise.then(function(res){
  // other logic, independent from task1
})
查看更多
登录 后发表回答