40

This is a short version of my code.

var Promise = require('bluebird');
var fs = Promise.promisifyAll(require("fs"));

if (conditionA) {
  fs.writeFileAsync(file, jsonData).then(function() {
    return functionA();
  });
} else {
  functionA();
}

Both conditions call functionA. Is there way to avoid else condition? I can do fs.writeFileSync but I am looking for a non-blocking solution.

vinayr
  • 11,026
  • 3
  • 46
  • 42
  • Promise is designed for async task control. Why use the sync fucntion? You can simply check the return value of `writeFileAsync`. –  Oct 28 '14 at 03:44

3 Answers3

64

I think you're looking for

(conditionA 
  ? fs.writeFileAsync(file, jsonData)
  : Promise.resolve())
.then(functionA);

which is short for

var waitFor;
if (conditionA)
    waitFor = fs.writeFileAsync(file, jsonData);
else
    waitFor = Promise.resolve(undefined); // wait for nothing,
                                          // create fulfilled promise
waitFor.then(function() {
    return functionA();
});
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • I'm wondering if this is a typescript or bluebird problem, but when I just simply do `Promise.resolve()` I get an error `error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '{ (onFulfill?: (value: Object) => U | PromiseLike, onReject?: (error: any) => U | PromiseLi...' has no compatible call signatures.` But `Promise.resolve({})` works. Any ideas? – ahong Dec 27 '18 at 05:55
  • 1
    @ahong that's a Typescript problem telling you that it can't derive the type for `Promise.resolve()` - try `Promise.resolve(undefined)` or casting it explicitly to whatever you want. – Bergi Dec 27 '18 at 11:27
10

While other suggestions here work, personally I prefer the following.

Promise.resolve(function(){
  if (condition) return fs.writeFileAsync(file, jsonData);
}())
.then()

It has the disadvantage of always creating this additional promise (rather minor IMO) but looks much cleaner to my eye. You can also add other conditions/logic easily inside the IIFE.

EDIT

After implementing things like this for a long time now I have definitely changed to something slightly clearer. The initial promise is created regardless so it is much clearer to simply do:

/* Example setup */

var someCondition = (Math.random()*2)|0;
var value = "Not from a promise";
var somePromise = new Promise((resolve) => setTimeout(() => resolve('Promise value'), 3000));


/* Example */

Promise.resolve()
.then(() => {
  if (someCondition) return value;
  return somePromise;
})
.then((result) => document.body.innerHTML = result);
Initial state
Actually, in the your case it would simply be
if (someCondition) return somePromise;

inside of the first .then() function.

Goblinlord
  • 3,290
  • 1
  • 20
  • 24
2

You could always use Promise.all() with conditional function

var condition = ...;

var maybeWrite = function(condition, file, jsonData){
    return (condition) ? fs.writeFileAsync(file, jsonData) : Promise.resolve(true);
}

Promise.all([maybeWrite(condition, file, jsonData),functionA()])
.then(function(){
    // here 'functionA' was called, 'writeFileAsync' was maybe called
})

Or, if you want functionA only called after the file maybe was written you can separate:

maybeWrite(condition, file, jsonData)
.then(function(){
    // here file may have been written, you can call 'functionA'
    return functionA();
})
aarosil
  • 4,848
  • 3
  • 27
  • 41
  • My *only* issue with this approach is maintainability. You sign yourself up for having to 'untangle' stuff down the line. Which is one of the benefits of Promise chains - your logic is linear feeling. – The Dembinski Jan 30 '17 at 19:36