Valverde Antonio
- Why Promises?
- What is a Promise?
- Promise standard
- Producing a Promise
- Consuming a Promise
- Instance methods
- then()
- catch()
- Static methods
- Promise.all()
- Promise.race()
- Promise.resolve()
- Promise.reject()
- Promise limitations
- Compatibility Promises/callbacks in libraries
- Quizes
- References
- Chaining is simpler
- Promise-based functions return results, they don’t continue execution via callbacks
- The caller stays in control
- Cleaner signatures
- With callbacks, the parameters of a function are mixed. With Promises all parameters are input
- Standardized
- Before promises: Node.js callbacks, XMLHttpRequest, IndexedDB, etc
- Call the callback more than once
- Call the callback too early
- Don’t call the callback
- Errors could create a synchronous reaction whereas nonerrors would be asynchronous
This makes callbacks not very trustable in some cases.
→ Promises are resolved only once by definition
→ The callback you provide to Promise instances then(..) method will always be called asynchronously
→ A timeout can be set using Promise.race(..)
→ Promises turn even JS exceptions (synchronous) into asynchronous behavior
-
Before the result is ready, the Promise is
pending
-
If a result is available, the Promise is
fulfilled
-
If an error happened, the Promise is
rejected
From now on I will speak about ES6 Native promises.
bluebird
https://github.com/petkaantonov/bluebird
Q
https://github.com/kriskowal/q
const p = new Promise(
function (resolve, reject) { // (A)
···
if (···) {
resolve(value); // success
} else {
reject(reason); // failure
}
});
const promise = returnPromise();
promise.then(
function fulfilled (result) {
console.log(result);
},
function rejected () {
// handle rejected promise
}
);
Accepts two callbacks parameters
→ In case something different from a function is passed as parameter, that then()
is ignored and the Promise chain continues.
const p = Promise.resolve(3)
.then(x => {})
.then(x => {
console.log(x);
});
p instanceof Promise // true
Promise.resolve(3)
.then(x => {})
.then(x => {
console.log(x);
});
Promise.resolve(3)
.then(x => {
return 4;
})
.then(x => {
console.log(x); // 4
});
// same as code above
const p = Promise.resolve(3)
.then(x => {
return 4;
});
// p contains a resolved promise with the value 4
p.then(x => {
console.log(x); // 4
});
Promise.resolve(3)
.then(x => {
return Promise.resolve(4);
})
.then(x => {
console.log(x);
});
Promise.resolve(3)
.then(x => {
return Promise.reject('ooops');
})
.then(x => {
console.log(x);
})
.catch(e => {
console.log(e);
});
Promise.resolve(3)
.then(x => {
throw new Error(‘omg’);
return 4;
})
.then(
x => {
console.log(x);
},
e => {
console.log(e);
}
);
promise.then(
null,
error => { /* rejection */ }
);
Above code is the same as the code below:
promise.catch(error => {
/* rejection */
});
done()
is implemented in some libraries, but not in ES6 Promises at the moment.
Accepts an iterable as parameter.
Returns a Promise that:
- Is fulfilled if all elements in iterable are fulfilled
- Fulfillment value: Array with fulfillment values
- Is rejected if any of the elements are rejected
- Rejection value: first rejection value
Promise.all([
asyncFunc1(),
asyncFunc2()
])
.then((results) => {
···
})
.catch(err => {
// Receives first rejection among the Promises
···
});
Native Array.prototype.map()
can be used:
const fileUrls = [
'http://example.com/file1.txt',
'http://example.com/file2.txt',
];
const promisedTexts = fileUrls.map(httpGet);
Promise.all(promisedTexts)
.then(texts => {
for (const text of texts) {
console.log(text);
}
})
.catch(reason => {
// Receives first rejection among the Promises
});
Accepts an iterable as parameter.
The first element of iterable that is settled is used to settle the returned Promise.
Promise.race([
httpGet('http://example.com/file.txt'),
delay(5000).then(function () {
throw new Error('Timed out')
});
])
.then(text => {
...
})
.catch(reason => {
// Receives first rejection among the Promises
});
- Value
- Promise
- Thenable
If x
is a value:
Promise.resolve('abc')
.then(x => console.log(x)); // abc
If x
is a Promise whose constructor is the receiver then x is returned unchanged:
const p = new Promise(() => null);
console.log(Promise.resolve(p) === p); // true
If x
is a thenable
, it is converted to a Promise.
→ A thenable
is an object that has a Promise-style then() method.
Promise.resolve(x)
makes sure we get a Promise result, so we can get a normalized, safe result we'd expect.
Returns a Promise that is rejected with err:
const myError = new Error('Problem!');
Promise.reject(myError)
.catch(err => console.log(err === myError)); // true
In the code below p1
and p2
have a rejected promise with the reason 'Ooops'
.
var p1 = new Promise( function(resolve,reject){
reject('Oops');
} );
var p2 = Promise.reject('Oops');
// `foo(..)`, `STEP2(..)` and `STEP3(..)` are
// all promise-aware utilities
var p = foo( 42 )
.then( STEP2 )
.then( STEP3 );
p.catch( handleErrors );
If any step of the chain in fact does its own error handling (perhaps hidden/abstracted away from what you can see), handleErrors(..)
won't be notified.
Promises by definition only have a single fulfillment value or a single rejection reason.
Promise.resolve(3)
.then(x => {
return [1, 2];
})
.then( function(msgs){
const x = msgs[0];
const y = msgs[1];
console.log( x, y );
});
Promise.resolve(3)
.then(x => {
return { a: 1, b: 2 };
})
.then(x => {
const a = x.a;
const b = x.b;
console.log(a, b);
});
Using ES6 destructuring we can avoid some boilerplate :
Promise.resolve(3)
.then(x => {
return [1, 2];
})
.then(([x, y]) => {
console.log(x, y);
});
Promise.resolve(3)
.then(x => {
return { a: 1, b: 2 };
})
.then(({ a, b }) => {
console.log(a, b);
});
Once you create a Promise and register a fulfillment and/or rejection handler for it, there's nothing external you can do to stop that progression.
As a convention, usually a Promise is returned if no callback is passed.
collection.find().toArray((err, docs) => {
if (err) {
// err handling
}
console.log(docs):
});
collection.find().toArray().then(
docs => { console.log(docs); },
err => { // err handling }
);
const p = Promise.resolve()
p.then( function a() {
p.then( function c() {
console.log('C');
} );
console.log('A');
} );
console.log('D');
p.then( function b() {
console.log('B');
} );
console.log('F');
const doSomethingElse = () => {
return Promise.resolve('hola');
};
const finalHandler = (message) => {
console.log(message);
};
Promise.resolve()
.then(() => {
return doSomethingElse();
})
.then(finalHandler);
const doSomethingElse = () => {
return Promise.resolve('hola');
};
const finalHandler = (message) => {
console.log(message);
};
Promise.resolve()
.then(() => {
doSomethingElse();
})
.then(finalHandler);
const doSomethingElse = () => {
return Promise.resolve('hola');
};
const finalHandler = (message) => {
console.log(message);
};
Promise.resolve()
.then(doSomethingElse())
.then(finalHandler);
const doSomethingElse = () => {
return Promise.resolve('hola');
};
const finalHandler = (message) => {
console.log(message);
};
Promise.resolve()
.then(doSomethingElse)
.then(finalHandler);
Promise.resolve('hola')
.then(
function fulfilled (msg) {
msg.type.error;
console.log(msg);
},
function rejected (err) {
console.log('caught error:', err);
}
);
Promise.resolve('hola')
.then(function fulfilled (msg) {
msg.type.error;
console.log(msg);
})
.catch(function rejected (err) {
console.log('caught error:', err);
});
-
You Don't Know JS: Async & Performance (Kyle Simpson) https://github.com/getify/You-Dont-Know-JS/blob/master/async%20%26%20performance/ch3.md
-
Exploring ES6 (Axel Rauschmayer) http://exploringjs.com/es6/ch_promises.html
-
pouchdb blog: We have a problem with promises (Nolan Lawson) https://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html
-
JavaScript reference documentation https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise