原文链接 Medium - Master the JavaScript Interview: What is a Promise?
Straight to the point, what is Promise?
A promise
means that a point might produce a single value of the object at some time in the future: whether the cause is a value resolved or not resolved a value (such as network error occurred). A promise
may be fulfilled
, rejected
or pending
one of three states. promise
The user can use the callback function to process fulfilled
and rejected
state.
Promise
It can be quite enthusiastic, once its constructor is called, promise
it will immediately start to do any task you give it.
A less complete development history of promise
Early on promise
and futures
implement (the two concepts similar / related) began as MultiLisp
and Concurrent Prolog
language appeared in the early 1980s. promise
Use of the term was created by Barbara Liskov and Liuba Shrira in 1988 out of 1 .
The first time I know in JavaScript promise
when this concept Node
is only just emerging, the community was also discussing the best way to implement asynchronous behavior positive. For a time, communities have used promises
this concept, but final implementation of the standard Node
error handling callback.
Almost at the same time, Dojo
the framework by Deferred API
adding a promises
. With this continued rising public interest and activity, and ultimately the formation of a new promise/A
specification allows multiple promises
implementations to be unified.
jQuery asynchronous behavior is reconstructed around promises. jQuery and Dojo support for the promise of Deferred
extremely similar, because of its massive audience quickly became the most popular promise JavaScript implementation. However, jQuery does not support fulfilled/rejected
chained two-channel call behavior and exception handling, and these features are also user basis upon which to build applications using the promise.
Despite the presence of the jQuery shortcomings, it still became the mainstream JavaScript promises are realized, such as the degree favorite ahead Q
, When
or Bluebird
such promise libraries. jQuery for incompatibilities spawned a number of supplement to the promise to form a Promises/A+
specification.
ES6 The Promise brought on Promises A specification is fully compatible / +, and there are some very important new standards-based API also be supported Promise: The most common are WHATWG Fetch
norms and standard asynchronous function.
Described herein is consistent with the promises Promises A, is / + ECMAScript standard specifications Promise
achieved.
How is the work of Promise
is a promise to return from a function of the asynchronous asynchronous object, which can be in one of three states:
- Fulfilled:
onFulfilled()
will be called (for example,resolve()
to be called) - Rejected:
onRejected()
will be called (for example,reject()
to be called) - Pending: have not yet
fulfilled
orrejected
As long as the promise of a state that is pending on behalf of its status has been determined (resolved or rejected), sometimes people will use resolved and settled to mean the same thing: a non-pending status.
Once the status is determined, promise the state can no longer be changed, call resolve()
, or reject()
it will not have any effect. It has determined that a state of immutability promise is one of the important characteristics.
Native JS promise not exposed outside the state. In fact, you might prefer to think of it as a black box mechanism to look at. Only when the role of a function is created when a promise, either to visit or reject that we need to resolve deep promise of state.
The following function after a specified time resolve, and then returns a promise:
const wait = time => new Promise((resolve) => setTimeout(resolve, reject))
wait(3000).then(() => console.log('Hello!'));
复制代码
Here call wait(3000)
will print out after waiting for 3000ms Hello!
. All the promises are standard defines a .then()
method, this method can be passed a handle, so that to get the value of resolve or reject.
ES6 promise constructor receives a function as a parameter. This function receives two parameters, respectively resolve()
, and reject()
. In the above example, we only used the resolve()
then called setTimeout()
to create a delay function, and finally call the delay function is completed resolve()
.
You can select only pass resolve()
or reject()
a value, the value is passed to .then()
the callback function.
Whenever I to reject()
value in a pass, I will pass a Error object inside. In general I expect two solutions states: normal happy ending, or throw an exception. Error object into a transfer will make the results more uncertain.
Several important rules Promise
promises have been made standard specification Promises / A + community defined. A wide variety of existing implementations comply with the specification, and these include the JavaScript standard ECMAScript promises.
Follow these guidelines, and promises must contain the following rules:
- A promise / thenable object must provide a compatible standard
.then()
method; - promise to be in a pending state fulfilled or rejected two states;
- A promise is fulfilled or state once they have been rejected, they can not be changed to another state;
- Once finalized state promise, it must have a value (even undefined). This value is not changed.
Each promise must have the following properties provide a .then()
method of:
promise.then(
onFulfilled?: Function,
onRejected?: Function
) => Promise
复制代码
.then()
Method must comply with the following rules:
onFulfilled()
AndonRejected()
are all optional parameters;- If the argument provided is not a function parameter is ignored;
onFulfilled()
Invoked when the state becomes the promise fulfilled, the value will be returned promise as the first argument;onRejected()
Will be called when the state becomes a promise rejected, the reasons will be rejected as the first argument. The reason could be any valid JavaScript value, but was rejected due basically equivalent to throwing an exception, so I recommend using the Error object;onFulfilled()
AndonRejected()
it will not be called multiple times;.then()
It may be called multiple times on the same promise. In other words, promise can be used to merge callback function;.then()
It must return a new promise, to be calledpromise2
;- If
onFulfilled()
oronRejected()
return a valuex
,x
a Promise,promise2
will be usedx
to lock. Otherwise,promise2
it will be the valuex
fulfilled. - If you
onFulfilled()
oronRejected()
throw an exceptione
,promise2
it muste
be rejected as the cause; - If
onFulfilled()
not function,promise1
is Fulfilled, itpromise2
must be Fulfilled same value; - If
onRejected()
not a function,promise1
to be rejected, itpromise2
must be rejected by the same reason;
Promise chained calls
Due to .then()
always return a new promise, so that you can achieve the promise of a chain of errors for precise control. Promises allows us to mimic the normal synchronization code behavior (such as try ... catch).
Like synchronization code as the chain calls can be effective execution of the order. For example, the following:
fetch(url)
.then(process)
.then(save)
.catch(handleErrors)
;
复制代码
Assumptions above fetch()
, process()
, save()
return promises, process()
waits fetch()
finished before you begin, empathy save()
must wait for process()
execution is finished before you begin, handleErrors()
when any one of the promises run and only the current face of the error will be performed.
The following example is given a complex:
const wait = time => new Promise(
res => setTimeout(() => res(), time)
);
wait(200)
// onFulfilled() 可以返回一个新的 promise, `x`
.then(() => new Promise(res => res('foo')))
// 下一个 promise 会假设 `x`的状态
.then(a => a)
// 上面我们返回了未被包裹的`x`的值
// 因此上面的`.then()`返回了一个 fulfilled promise
// 有了上面的值之后:
.then(b => console.log(b)) // 'foo'
// 需要注意的是 `null` 是一个有效的 promise 返回值:
.then(() => null)
.then(c => console.log(c)) // null
// 至此还未报错:
.then(() => {throw new Error('foo');})
// 相反, 返回的 promise 是 rejected
// error 的原因如下:
.then(
// 由于上面的 error导致在这里啥都没打印:
d => console.log(`d: ${ d }`),
// 现在我们处理这个 error (rejection 的原因)
e => console.log(e)) // [Error: foo]
// 有了之前的异常处理, 我们可以继续:
.then(f => console.log(`f: ${ f }`)) // f: undefined
// 下面的代码未打印任何东西. e 已经被处理过了,
// 所以该句柄并未被调用:
.catch(e => console.log(e))
.then(() => { throw new Error('bar'); })
// 当一个 promise 被 rejected, success 句柄就被跳过.
// 这里因为 'bar' 异常而不打印任何东西:
.then(g => console.log(`g: ${ g }`))
.catch(h => console.log(h)) // [Error: bar]
;
复制代码
Error Handling
Note that promise has both handle success and failure, so the wording is common following code:
save().then(
handleSuccess,
handleError
)
复制代码
But if the handleSuccess()
wrong how to do? From the .then()
return of the promise will be rejected, but there is no follow-up function can capture the error message - the meaning is a mistake in your app is swallowed, it can be a little bad.
For the above reasons, some people will be referred to as an anti-code above mode (anti-pattern), and recommend alternative wording as follows:
save()
.then(handleSuccess)
.catch(handleError);
复制代码
Where the difference is subtle, but important. In a first example, from save()
errors will be caught, but from the handleSuccess()
errors will be swallowed.
.catch()
it handles from either
save()
or
handleSuccess()
wrong in.
Of course, from the
save()
mistakes there may be a network error, but
handleSuccess()
the error may come from a developer forgot processing error status code, if you want these two errors are handled differently how to do? This approach can then choose the following:
save()
.then(
handleSuccess,
handleNetworkError
)
.catch(handleProgrammerError)
复制代码
Either way you prefer, I recommend you bring back all the Promises .catch()
.
How do I cancel / interrupt a Promise
Just learned how to use the promise of users there is always a lot of questions, most of which is about how to cancel / interrupt a promise. The idea is this: you want to go directly to reject cancellation / interruption of promise, the reason to write "Cancelled" can be. But if you come to if you want to distinguish it from conventional error handling area, then go to develop their own error handling branches.
Listed below are some people write error cancellation / interruption promise often made of:
Promise to add a.cancel()
Add .cancel()
makes promise of non-standardized, but also contrary to the provisions of another promise: only have the ability to create a function of the promise to resolve, reject or cancel / interrupt the promise. Such an approach will destroy the package propagation characteristic function, operation encourage people to promise not the right place in the code undermine the promise.
Forget cleanup
Some smart people figure out a use promise.race()
way to cancel / interrupt promise. The problem with this approach is that interrupt operation control function is created by the promise of launch, which is the only one proper position to clean up actions, such as cleaning up timer or to free memory by releasing a reference to the data, etc. Wait.
Forget being a reject treatment interruption promise
Do you know the warning message when you forget to deal with a promise of state refused to throw full Chrome console do?
Rethinking promise Cancellation / Interruption
In general, I will promise to put all the information needed to pass it all to promise decide how to resolve / reject / cancel when a promise is created. This embodiment does not require a .cancel()
method of adhering to the promise. You probably want to know is how do you know if you know it is going to be canceled when the promise created.
We want to spread the decision whether or not to cancel the promise of value can be themselves, it might look like this:
const wait = (
time,
cancel = Promise.reject()
) => new Promise((resolve, reject) => {
const timer = setTimeout(resolve, time);
const noop = () => {};
cancel.then(() => {
clearTimeout(timer);
reject(new Error('Cancelled'));
}, noop);
});
const shouldCancel = Promise.resolve(); // Yes, cancel
// const shouldCancel = Promise.reject(); // No cancel
wait(2000, shouldCancel).then(
() => console.log('Hello!'),
(e) => console.log(e) // [Error: Cancelled]
);
复制代码
This uses the default parameter assignment to tell it the default is not canceled. Such that the cancel
parameter is optional. Then we set a timer, here we get the timer ID in order to cancel it in the back.
We used cancel.then()
to handle the cancellation / interruption and clean up resources. Its operating condition is canceled before the resolve to make promise. If you cancel too late, you will miss the opportunity to cancel.
You may be more curious about noop
the role of function is what the word represents noop-op, which means consequently do. If you do not specify this function, V8 engine will throw a warning: UnhandledPromiseRejectionWarning: Unhandled promise rejection
so always remember to deal with rejection promise is a good habit, even if your handle is noop.
Abstract promise Cancellation / Interruption
The above wait()
timer of course, is excellent, but we want to continue this line of thinking further above abstraction to encapsulate everything you need to know:
- The default reject require interruption of promise
- Remember to clean up the promise had been reject
- Vigilant
onCancel
cleanup operation itself is also likely to throw exception, which also need to be addressed.
Let's promise to create a tool function can interrupt it, so that you can use to wrap up any promise. Form as follows:
speculation(fn: SpecFunction, shouldCancel: Promise) => Promise
复制代码
SpecFunction
As you pass the constructor function Promise same, the only difference is that it has a onCancel
handle:
SpecFunction(resolve: Function, reject: Function, onCancel: Function) => Void
复制代码
const speculation = (
fn,
cancel = Promise.reject()
) => new Promise((resolve, reject) => {
const noop = () => {};
const onCancel = (
handleCancel
) => cancel.then(
handleCancel,
noop
)
.catch(e => reject(e))
;
fn(resolve, reject, onCancel);
});
复制代码
But its effect on patients essence, there is also need to consider your boundaries if necessary. I wrote a full version for your reference, speculation
Epilogue
Article is too long, the second half really translate to translate it no longer, mainly their own understanding of the promise is not deep enough, the back can not understand, but still feel the need to stay the course, this thing done, get it back again back perfect, never giveup!
references
[1] Barbara Liskov; Liuba Shrira (1988). “Promises: Linguistic Support for Efficient Asynchronous Procedure Calls in Distributed Systems”. Proceedings of the SIGPLAN ’88 Conference on Programming Language Design and Implementation; Atlanta, Georgia, United States, pp. 260–267. ISBN 0–89791–269–1, published by ACM. Also published in ACM SIGPLAN Notices, Volume 23, Issue 7, July 1988.