[Translation] JavaScript Fun Interview: What is Promise?

原文链接 Medium - Master the JavaScript Interview: What is a Promise?

Straight to the point, what is Promise?

A promisemeans 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 promisemay be fulfilled, rejectedor pendingone of three states. promiseThe user can use the callback function to process fulfilledand rejectedstate.

PromiseIt can be quite enthusiastic, once its constructor is called, promiseit will immediately start to do any task you give it.

A less complete development history of promise

Early on promiseand futuresimplement (the two concepts similar / related) began as MultiLispand Concurrent Prologlanguage appeared in the early 1980s. promiseUse of the term was created by Barbara Liskov and Liuba Shrira in 1988 out of 1 .

The first time I know in JavaScript promisewhen this concept Nodeis only just emerging, the community was also discussing the best way to implement asynchronous behavior positive. For a time, communities have used promisesthis concept, but final implementation of the standard Nodeerror handling callback.

Almost at the same time, Dojothe framework by Deferred APIadding a promises. With this continued rising public interest and activity, and ultimately the formation of a new promise/Aspecification allows multiple promisesimplementations to be unified.

jQuery asynchronous behavior is reconstructed around promises. jQuery and Dojo support for the promise of Deferredextremely similar, because of its massive audience quickly became the most popular promise JavaScript implementation. However, jQuery does not support fulfilled/rejectedchained 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, Whenor Bluebirdsuch 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 Fetchnorms and standard asynchronous function.

Described herein is consistent with the promises Promises A, is / + ECMAScript standard specifications Promiseachieved.

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 fulfilledorrejected

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()And onRejected()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()And onRejected()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 called promise2;
  • If onFulfilled()or onRejected()return a value x, xa Promise, promise2will be used xto lock. Otherwise, promise2it will be the value xfulfilled.
  • If you onFulfilled()or onRejected()throw an exception e, promise2it must ebe rejected as the cause;
  • If onFulfilled()not function, promise1is Fulfilled, it promise2must be Fulfilled same value;
  • If onRejected()not a function, promise1to be rejected, it promise2must 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.

In the second example, .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 cancelparameter 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 noopthe 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 rejectionso 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:

  1. The default reject require interruption of promise
  2. Remember to clean up the promise had been reject
  3. Vigilant onCancelcleanup 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
复制代码

SpecFunctionAs you pass the constructor function Promise same, the only difference is that it has a onCancelhandle:

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.

Guess you like

Origin blog.csdn.net/weixin_34033624/article/details/91391170