Open rogerxu opened 7 years ago
Master the JavaScript Interview: What is a Promise? – JavaScript Scene – Medium
3 states
fetch(url)
.then(process)
.then(save)
.catch(handleErrors)
;
Assuming each of the functions, fetch(), process(), and save() return promises, process() will wait for fetch() to complete before starting, and save() will wait for process() to complete before starting. handleErrors() will only run if any of the previous promises reject.
const wait = time => new Promise(
res => setTimeout(() => res(), time)
);
wait(200)
// onFulfilled() can return a new promise, `x`
.then(() => new Promise(res => res('foo')))
// the next promise will assume the state of `x`
.then(a => a)
// Above we returned the unwrapped value of `x`
// so `.then()` above returns a fulfilled promise
// with that value:
.then(b => console.log(b)) // 'foo'
// Note that `null` is a valid promise value:
.then(() => null)
.then(c => console.log(c)) // null
// The following error is not reported yet:
.then(() => {throw new Error('foo');})
// Instead, the returned promise is rejected
// with the error as the reason:
.then(
// Nothing is logged here due to the error above:
d => console.log(`d: ${ d }`),
// Now we handle the error (rejection reason)
e => console.log(e)) // [Error: foo]
// With the previous exception handled, we can continue:
.then(f => console.log(`f: ${ f }`)) // f: undefined
// The following doesn't log. e was already handled,
// so this handler doesn't get called:
.catch(e => console.log(e))
.then(() => { throw new Error('bar'); })
// When a promise is rejected, success handlers get skipped.
// Nothing logs here because of the 'bar' exception:
.then(g => console.log(`g: ${ g }`))
.catch(h => console.log(h)) // [Error: bar]
;
Recommend ending all promise chains with a .catch()
.
save()
.then(
handleSuccess,
handleNetworkError
)
.catch(handleProgrammerError)
;
onCancel
cleanup might itself throw an error, and that error will need handling, too. (Note that error handling is omitted in the wait example above — it’s easy to forget!)// HOF Wraps the native Promise API
// to add take a shouldCancel promise and add
// an onCancel() callback.
const speculation = (
fn,
cancel = Promise.reject() // Don't cancel by default
) => new Promise((resolve, reject) => {
const noop = () => {};
const onCancel = (
handleCancel
) => cancel.then(
handleCancel,
// Ignore expected cancel rejections:
noop
)
// handle onCancel errors
.catch(e => reject(e))
;
fn(resolve, reject, onCancel);
});
Use
const wait = (
time,
cancel = Promise.reject() // By default, don't cancel
) => speculation((resolve, reject, onCancel) => {
const timer = setTimeout(resolve, time);
// Use onCancel to clean up any lingering resources
// and then call reject(). You can pass a custom reason.
onCancel(() => {
clearTimeout(timer);
reject(new Error('Cancelled'));
});
}, cancel); // remember to pass in cancel!
wait(200, wait(500)).then(
() => console.log('Hello!'),
(e) => console.log(e)
); // 'Hello!'
wait(200, wait(50)).then(
() => console.log('Hello!'),
(e) => console.log(e)
); // [Error: Cancelled]
Promises: All The Wrong Ways | getiblog - 众成翻译
// anti-pattern
foo()
.then(nextStep);
// good
Promise.resolve(foo())
.then(nextStep);
Promise.resolve()
.then(firstStep);
// good
firstStep();
// bad
new Promise(function(resolve, reject) {
firstStep()
.then(resolve, reject);
});
If firstStep()
results in an exception, it will automatically be caught by the Promise machinery, and turned into a promise rejection that the rest of the chain can articulate handling for.
The async function normalizes whatever comes back from the firstStep() function call into a promise, including catching any exception and turning it into a promise rejection.
async () => firstStep()()
.then();
Essentially, this approach treates a promise like a single-value event conduit. One part of the app holds the “write end” of the pipe, the deferred
. Another part of the app holds the “read end”, the promise.
But it’s an anti-pattern and that you should try to avoid it. At a minimum, if you have to do so, you should be hiding that kind of trick inside the plumbing of a library, not exposing the deferred and promise separately at the top level of logic in your application.
function makePromise() {
var def = {};
var pr = new Promise(function init(resolve,reject) {
def.resolve = resolve;
def.reject = reject;
});
return [pr, def];
}
var [pr, def] = makePromise();
We can use already built-in parts of JavaScript to create a pull abstraction directly. A generator can create a producer iterator:
function *setupProducer() {
var pr = new Promise(function(resolve, reject){
setupEvent(resolve, reject);
});
yield pr;
}
var producer = setupProducer();
// Here’s how we later pull a value:
var pr = producer.next();
pr.then(function(value) {
// ..
});
We yield a promise for that single next value that the producer will eventually produce. So the consumer is pulling (iterating) out each push instance (promise).
// bad
Promise.resolve().then(function(){
console.log("one");
});
Promise.resolve().then(function(){
console.log( "two" );
});
// bad
var fn;
var pr = new Promise(function init(resolve){
fn = resolve;
});
fn(42);
The above snippet is equivalent to just:
var pr = Promise.resolve(42);
// bad, side-effect programming
function getOrderDetails(orderID) {
var _order;
return db.find("orders", orderID)
.then(function(order) {
_order = order;
return db.find("customers", order.customerID )
})
.then(function(customer) {
_order.customer = customer;
return _order;
});
}
function getOrderDetails(orderID) {
return db.find("orders", orderID)
.then(function(order) {
return db.find("customers", order.customerID)
// nested `then()` instead of flattened to outer chain,
// so that we still have lexical scope access to `order`
.then(function(customer) {
order.customer = customer;
return order;
});
});
}
Normally, nesting promise then(..)
s is a bad idea, and even goes by the name “promise hell”.
But in these cases where multiple values need to be available (in scope), a flattened vertical promise chain necessitates the worser evil of side-effects. Promise nesting is the better choice.
// bad
firstStep()
.then(secondStep)
.then(thirdStep);
Use the synchronous-async pattern.
async function main() {
await firstStep();
await secondStep();
await thirdStep();
}
async function main() {
try {
var val1 = await firstStep();
var val2 = await secondStep(val1);
var val3 = await thirdStep(val1, val2);
console.log("Final: ", val3);
} catch (err) {
console.error(err);
}
}
For the most part, we should try not to ever call then(..)
explicitly. As much as possible, that should stay hidden as an implementation detail.
Calling then(..)
on a promise is a code smell and anti-pattern.
How to escape Promise Hell – Ronald Chen – Medium
.then()
with linearly dependent PromisesBad
fetchBook()
.then((book) => {
return formatBook(book)
.then((book) => {
return sendBookToPrinter(book);
});
});
Good
fetchBook()
.then(formatBook)
.then(sendBookToPrinter);
.then()
with independent PromisesBad
demandMoreDonuts()
.then(() => {
return greet('fred')
.then(() => {
return closeGate();
});
});
Good - No order
Promise.all([
demandMoreDonuts(),
greet('fred'),
closeGate(),
]);
Good - serialize the order
Promise.resolve()
.then(demandMoreDonuts)
.then(() => greet('fred'))
.then(closeGate);
]);
.then()
with multiple dependent PromisesBad
connectDatabase()
.then((database) => {
return findAllBooks(database)
.then((books) => {
return getCurrentUser(database)
.then((user) => {
return pickTopRecommentations(books, user);
});
});
});
Good
const databasePromise = connectDatabase();
const booksPromise = databasePromise.then(findAllBooks);
const userPromise = databasePromise.then(getCurrentUser);
Promise.all([
booksPromise,
userPromise,
]).then(([books, user]) => pickTopRecommentations(books, user));
You're Missing the Point of Promises
deferred.promise() | jQuery API Documentation - Return a Deferred's Promise object.
三個不要再用 jQuery Promise 的理由 | Pymaster
jQuery.Deferred().reject()
.then(null, function () {
return 'error handled';
})
.then(function (res) {
log('jquery resolve', res);
}, function (res) {
log('jquery reject', res);
});
promise chain 中被 reject,经过 rejection handler 后
Coming from jQuery · kriskowal/q Wiki
转换 jQuery Promise 的最简单方法就是直接用 Promise 把它包起来 (resolve):
Promise.resolve(jqPromise).then(...)
JavaScript Promises: an Introduction | Web | Google Developers
Promise - JavaScript | MDN
Using promises - JavaScript | MDN
JavaScript Promise迷你书(中文版) · 看云