Use Promise

 

Use Promise - JavaScript | MDN

Using Promises

 

A Promise is an object representing the eventual completion or failure of an asynchronous operation. Since most people are consumers of already-created promises, this guide will explain consumption of returned promises before explaining how to create them.

Promise is an object that represents the ultimate or failure of a complete asynchronous operation. Because most people just use Promise instance of an object has been created, so this tutorial will first explain how to use the Promise, and then explain how to create a Promise.


Essentially, a promise is a returned object to which you attach callbacks, instead of passing callbacks into a function.

Promise is essentially a function object returned, we can bind callbacks on it, so we do not need at the outset to the callback function as a parameter of the function.


Imagine a function, createAudioFileAsync(), which asynchronously generates a sound file given a configuration record and two callback functions, one called if the audio file is successfully created, and the other called if an error occurs.

Suppose now that there is a function called createAudioFileAsync (), which receives a number of configuration and two callback functions, and then generating an audio file asynchronously. A callback function is called when the file is successfully created, the other is called when an exception occurs.


Here's some code that uses createAudioFileAsync():

The following is an example using createAudioFileAsync () is:

 

// success callback 
function successCallback (the Result) {
console.log("Audio file ready at URL: " + result);
}

// failed callback 
function failureCallback (error) {
console.error("Error generating audio file: " + error);
}

createAudioFileAsync(audioSettings, successCallback, failureCallback);

 

…modern functions return a promise you can attach your callbacks to instead:

More modern function returns a promise objects so that you your callback function can be bound on the promise.


If createAudioFileAsync() were rewritten to return a promise, using it could be as simple as this:

If the function createAudioFileAsync () is rewritten in the form of promise to return, then we can simply call it to the following:

const promise = createAudioFileAsync(audioSettings); 
promise.then(successCallback, failureCallback);

// or for short 
createAudioFileAsync (audioSettings) .then (successCallback, failureCallback);

 

We call this an asynchronous function call. This convention has several advantages. We will explore each one.

We call this asynchronous function call, this form has several advantages, one by one we will discuss below.

 

Guarantees

Promise

Unlike "old-style", passed-in callbacks, a promise comes with some guarantees:

Different from the "old" incoming callback when using the Promise, will have the following convention:

  • Callbacks will never be called before the completion of the current run of the JavaScript event loop.
  • In the round before the event loop completes, the callback function will not be called.
  • Callbacks added with then() even after the success or failure of the asynchronous operation, will be called, as above.
  • Even if the asynchronous operation has been completed (success or failure), after which by then () will add a callback function is called.
  • Multiple callbacks may be added by calling then() several times. Each callback is executed one after another, in the order in which they were inserted.
  • You can add as many callbacks multiple calls then (), which performs insertion order.

Of the About Things at The Great One a using Promises IS Chaining.
Promise great thing is chained calls (chaining).

 

Chaining

Chained calls

A common need is to execute two or more asynchronous operations back to back, where each subsequent operation starts when the previous operation succeeds, with the result from the previous step. We accomplish this by creating a promise chain.

Performing two or more consecutive asynchronous operations is a common requirement, after a successful operation is performed on the next operation start, and with the step of the operation result returned. We can achieve this by creating a Promise demand chain.


Here's the magic: the then() function returns a new promise, different from the original:

Witness the miracle of the moment: then () function returns a different and original new Promise:

const promise = doSomething();
const promise2 = promise.then(successCallback, failureCallback);

const promise2 = doSomething().then(successCallback, failureCallback);


This second promise (promise2) represents the completion not just of doSomething(), but also of the successCallback or failureCallback you passed in, which can be other asynchronous functions returning a promise. When that's the case, any callbacks added to promise2 get queued behind the promise returned by either successCallback or failureCallback.

promise2 only represents doSomething () function to complete, also represents complete or failureCallback successCallback you pass, these two functions can also return a Promise object to form another asynchronous operation, this is the case, the new on promise2 the callback function will be at the back of the Promise object.


Basically, each promise represents the completion of another asynchronous step in the chain.
Essentially, each represents a completion Promise another asynchronous chain process.

In the old days, doing several asynchronous operations in a row would lead to the classic callback pyramid of doom:
in the past, want to do multiple asynchronous operations will lead to the classic callback hell:

doSomething(function(result) {
  doSomethingElse(result, function(newResult) {
    doThirdThing(newResult, function(finalResult) {
      console.log('Got the final result: ' + finalResult);
    }, failureCallback);
  }, failureCallback);
}, failureCallback);

 

With modern functions, we attach our callbacks to the returned promises instead, forming a promise chain:

Now, we can return the callback bound to the Promise, forming a chain of Promise:

doSomething()
.then(function(result) {
  return doSomethingElse(result);
})
.then(function(newResult) {
  return doThirdThing(newResult);
})
.then(function(finalResult) {
  console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);

 

The arguments to then are optional, and catch(failureCallback) is short for then(null, failureCallback). You might see this expressed with arrow functions instead:

then in the parameter is optional, catch (failureCallback) is then (null, failureCallback) in abbreviated form. As shown below, we can also function represented by arrows:

doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.then (final result => {
  console.log(`Got the final result: ${finalResult}`);
})
.catch(failureCallback);

 

Important: Always return results, otherwise callbacks won't catch the result of a previous promise (with arrow functions () => x is short for () => { return x; }).

Note: Be sure to have a return value, otherwise, callback will not get a result on the Promise. (If the arrow function, () => x ratio () => {return x;} less verbose, but the latter retain the return wording only supports multiple statements.)

 

Chaining after a catch

Catch subsequent chaining

 

It's possible to chain after a failure, i.e. a catch, which is useful to accomplish new actions even after an action failed in the chain. Read the following example:

There may continue to use the chain operation after a failed callbacks, namely the use of a catch, which is thrown for a failure in the chain operation after a new operating again useful. Please read the following examples:

new Promise((resolve, reject) => {
    console.log('Initial');

    resolve();
})
.then(() => {
    throw new Error('Something failed');
        
    console.log('Do this');
})
.catch(() => {
    console.error('Do that');
})
.then(() => {
    console.log('Do this, no matter what happened before');
});

 

This will output the following text:

Output:

Initial
Do that
Do this, no matter what happened before


Note: The text Do this is not displayed because the Something failed error caused a rejection.

Note: Because an error was thrown Something failed, so the preceding execution Do this not being output.

 

Error propagation

Error propagation


You might recall seeing failureCallback three times in the pyramid of doom earlier, compared to only once at the end of the promise chain:

In the previous example, the callback hell, you might remember failureCallback call three times, and only once in the chain Promise call to tail.

doSomething()
.then(result => doSomethingElse(value))
.then(newResult => doThirdThing(newResult))
.then(finalResult => console.log(`Got the final result: ${finalResult}`))
.catch(failureCallback);


If there's an exception, the browser will look down the chain for .catch() handlers or onRejected. This is very much modeled after how synchronous code works:

Typically, an encounter exception is thrown, the browser will follow the link under the promise to find a onRejected failure or callback function specified by .catch () callback function. This procedure performs the following synchronization code is very similar.

try {
  let result = syncDoSomething();
  let newResult = syncDoSomethingElse(result);
  let finalResult = syncDoThirdThing(newResult);
  console.log(`Got the final result: ${finalResult}`);
} catch(error) {
  failureCallback(error);
}

 

This symmetry with asynchronous code culminates in the async/await syntactic sugar in ECMAScript 2017:

In ECMAScript 2017 standard async / await syntactic sugar, this symmetry and the synchronization code in the form of extreme manifestation obtained:

async function foo() {
  try {
    const result = await doSomething();
    const newResult = await doSomethingElse(result);
    const finalResult = await doThirdThing(newResult);
    console.log(`Got the final result: ${finalResult}`);
  } catch(error) {
    failureCallback(error);
  }
}

 

It builds on promises, e.g. doSomething() is the same function as before. You can read more about the syntax here.

This example is constructed on the basis of Promise, e.g., doSomething () function is the same as before. You can read more articles related to this syntax.

Promises solve a fundamental flaw with the callback pyramid of doom, by catching all errors, even thrown exceptions and programming errors. This is essential for functional composition of asynchronous operations.

By capturing all the errors, bugs and even throw an exception, Promise to solve the fundamental flaw callback hell. This basic functionality in terms of building asynchronous operation is necessary.

 

Promise rejection events

Promise reject event


Whenever a promise is rejected, one of two events is sent to the global scope (generally, this is either the window or, if being used in a web worker, it's the Worker or other worker-based interface). The two events are:

When Promise is rejected, there will be one of the following two events are distributed to the global scope (Typically, that window; if it is used in the web worker then, is Worker or other worker-based interface). These two events are as follows:


rejectionhandled

Sent when a promise is rejected, after that rejection has been handled by the executor's reject function.

When Promise is rejected, and the rejection after the reject will be distributed function processing this event.

 

unhandledrejection

Sent when a promise is rejected but there is no rejection handler available.

When Promise is rejected, but did not provide reject function to handle the rejection, it will distribute this event.

 

In both cases, the event (of type PromiseRejectionEvent) has as members a promise property indicating the promise that was rejected, and a reason property that provides the reason given for the promise to be rejected.

Both cases, PromiseRejectionEvent event has two properties, a promise is the property that points to the rejected Promise, another reason is the attribute for reasons Promise was rejected.

These make it possible to offer fallback error handling for promises, as well as to help debug issues with your promise management. These handlers are global per context, so all errors will go to the same event handlers, regardless of source.

Therefore, we can provide compensation process is Promise failure by the above events, but also conducive to debug problems related Promise. In each context, the process is global, so no matter what the source, all errors will be caught in the same handler in handling.


One case of special usefulness: when writing code for Node.js, it's common that modules you include in your project may have unhandled rejected promises. These get logged to the console by the Node runtime. You can capture these for analysis and handling by your code—or just to avoid having them cluttering up your output—by adding a handler for the unhandledrejection event, like this:

A particularly useful example: When you use Node.js, some rely rejected promises module may be untreated, these will be printed to the console at runtime. You can capture this information in your own code, and then add the appropriate handler unhandledrejection do the analysis and processing, or just to let you output neater. For example as follows:

window.addEventListener("unhandledrejection", event => {
  /* You might start here by adding code to examine the
     promise specified by event.promise and the reason in
     event.reason * / 
  / * Here you can add some code to check
     event.promise of promise and
     rejection of reason event.reason * /

  event.preventDefault();
}, false);

 

 

By calling the event's preventDefault() method, you tell the JavaScript runtime not to do its default action when rejected promises go unhandled. That default action usually involves logging the error to console, and this is indeed the case for Node.

Call the preventDefault event () method is to tell the JavaScript engine not to perform a default action when the promise is rejected, the default action will generally contain the error printed to the console.

Ideally, of course, you should examine the rejected promises to make sure none of them are actual code bugs before just discarding these events.

Ideally, before you ignore these events, we should check all rejected Promise, to confirm that this is not a code bug.

 

 

Creating a Promise around an old callback API

Promise legacy created in the API callback


A Promise can be created from scratch using its constructor. This should be needed only to wrap old APIs.

Promise Promise can be created by the constructor from scratch. This approach should be used only when wrapped in the old API's (by way of the constructor).


In an ideal world, all asynchronous functions would already return promises. Unfortunately, some APIs still expect success and/or failure callbacks to be passed in the old way. The most obvious example is the setTimeout() function:

Ideally, all asynchronous functions have returned to the Promise. But some still use the old API way to incoming success (or failure) of a callback. A typical example is setTimeout () function:

setTimeout(() => saySomething("10 seconds passed"), 10000);

 


Mixing old-style callbacks and promises is problematic. If saySomething() fails or contains a programming error, nothing catches it. setTimeout is to blame for this.

Callback mix old and run Promise may cause timing problems. If saySomething function fails, or contains a programming error, there is no way to capture it. It was strange setTimeout.


Luckily we can wrap setTimeout in a promise. Best practice is to wrap problematic functions at the lowest possible level, and then never call them directly again:

Fortunately, we can use the Promise to wrap it. The best practice is to package up these functions in question, stay at the bottom, and never to call them directly:

const wait = ms => new Promise(resolve => setTimeout(resolve, ms));

wait(10000).then(() => saySomething("10 seconds")).catch(failureCallback);

 

Basically, the promise constructor takes an executor function that lets us resolve or reject a promise manually. Since setTimeout() doesn't really fail, we left out reject in this case.

Typically, Promise constructor receives an execution function (executor), we can resolve and manually reject a function performed in this Promise. Since setTimeout not actually fails, then we can reject ignored in this case.

 

Composition

combination


Promise.resolve() and Promise.reject() are shortcuts to manually create an already resolved or rejected promise respectively. This can be useful at times.

Promise.resolve () and Promise.reject () is already resolve manually create a shortcut or reject the Promise. They are sometimes useful.


Promise.all() and Promise.race() are two composition tools for running asynchronous operations in parallel.

Promise.all () and Promise.race () is running in parallel two combined tool asynchronous operation.


We can start operations in parallel and wait for them all to finish like this:

We can initiate a parallel operation, and the like after the plurality of operation all over the next step, as follows:

 

Promise.all([func1(), func2(), func3()])
.then(([result1, result2, result3]) => { /* use result1, result2 and result3 */ });

 

Sequential composition is possible using some clever JavaScript :
You can use some clever wording to achieve timing JavaScript combination:

[func1, func2, func3].reduce((p, f) => p.then(f), Promise.resolve())
.then(result3 => { /* use result3 */ });

 

Basically, we reduce an array of asynchronous functions down to a promise chain equivalent to:
In general, when we call a function of the composition by the asynchronous array corresponds to a recursive Promise chain:

Promise.resolve().then(func1).then(func2).then(func3);


This can be made into a reusable compose function, which is common in functional programming:

We can also be written in the form of reusable functions, which is very common in functional programming:

const applyAsync = (acc,val) => acc.then(val);
const composeAsync = (...funcs) => x => funcs.reduce(applyAsync, Promise.resolve(x));

 

The composeAsync() function will accept any number of functions as arguments, and will return a new function that accepts an initial value to be passed through the composition pipeline:

composeAsync () function will accept any number as a function of its argument, and returns a new function that accepts a composition pipeline passing through the initial value. This is very useful for us, because any function can be asynchronous or synchronous, they can be executed in order to ensure that:

const transformData = composeAsync(func1, func2, func3);
const result3 = transformData(data);

 

In ECMAScript 2017, sequential composition can be done more simply with async/await:

In ECMAScript 2017 standard, the timing patterns can be easier by using async / await:

let result;
for (const f of [func1, func2, func3]) {
  result = await f(result);
}
/* use last result (i.e. result3) */

 

Timing

Timing

To avoid surprises, functions passed to then() will never be called synchronously, even with an already-resolved promise:

To avoid accidents, even if a state has become the resolve of Promise, the transfer function to then () will also always be an asynchronous call:

Promise.resolve().then(() => console.log(2));
console.log(1); // 1, 2

 

Instead of running immediately, the passed-in function is put on a microtask queue, which means it runs later when the queue is emptied at the end of the current run of the JavaScript event loop, i.e. pretty soon:

Passed to the then () function is placed in a micro-task queue, and not immediately, which means it is in the JavaScript event after the end of the run queue of all, the event queue is emptied before you begin:

const wait = ms => new Promise(resolve => setTimeout(resolve, ms));

wait().then(() => console.log(4));
Promise.resolve().then(() => console.log(2)).then(() => console.log(3));
console.log(1); // 1, 2, 3, 4

 

Nesting

Nesting


Simple promise chains are best kept flat without nesting, as nesting can be a result of careless composition. See common mistakes.

Simple Promise program is best to keep the chain flat, do not nest Promise, because the nest is often caused by carelessness. Examples of common errors can review the following section in.

Nesting is a control structure to limit the scope of catch statements. Specifically, a nested catch only catches failures in its scope and below, not errors higher up in the chain outside the nested scope. When used correctly, this gives greater precision in error recovery:

Promise wording nested control structures can limit a scope of the catch clause. Specifically, the nested catch just before its capture must also be its scope failureres, but not catch outside its chain outside the nest or error domain. If used correctly, it can achieve high-precision bug fixes.

doSomethingCritical()
.then(result => doSomethingOptional(result)
  .then(optionalResult => doSomethingExtraNice(optionalResult))
  .catch(e => {})) // Ignore if optional stuff fails; proceed.
.then(() => moreCriticalStuff())
.catch(e => console.error("Critical failure: " + e.message));

 

Note that the optional steps here are nested, not from the indentation, but from the precarious placement of the outer ( and ) around them.

Note that some of the code is nested steps, rather than a simple plain chain, these statements before and after are in parentheses () wrapped.

The inner neutralizing catch statement only catches failures from doSomethingOptional() and doSomethingExtraNice(), after which the code resumes with moreCriticalStuff(). Importantly, if doSomethingCritical() fails, its error is caught by the final (outer) catch only.

This internal catch statement can only be captured doSomethingOptional () and doSomethingExtraNice () fails, then restored to moreCriticalStuff () operation. Important: If doSomethingCritical () fails, the error will be caught only the last (external) catch statement to.

 

Common mistakes

Common Errors

Here are some common mistakes to watch out for when composing promise chains Several of these mistakes manifest in the following example:.
When writing Promise chain, need to pay attention to the error shown in the following example:

// Bad example! Spot 3 mistakes!

doSomething().then(function(result) {
  doSomethingElse(result) // Forgot to return promise from inner chain + unnecessary nesting
  .then(newResult => doThirdThing(newResult));
}).then(() => doFourthThing());
// Forgot to terminate chain with a catch!

 

The first mistake is to not chain things together properly. This happens when we create a new promise but forget to return it. As a consequence, the chain is broken, or rather, we have two independent chains racing. This means doFourthThing() won't wait for doSomethingElse() or doThirdThing() to finish, and will run in parallel with them, likely unintended. Separate chains also have separate error handling, leading to uncaught errors.

The first error is not correctly connected things. When we create a new Promise but forgot to return it, this happens. Thus, the chain is broken, or rather, we have two separate chains competition (while performing a two asynchronous execution instead of one). This means doFourthThing () will not wait doSomethingElse () or doThirdThing () is completed, and will run in parallel with them, may be unintentional. Separate strands have a separate error handling, the error did not result in capture.


The second mistake is to nest unnecessarily, enabling the first mistake. Nesting also limits the scope of inner error handlers, which—if unintended—can lead to uncaught errors. A variant of this is the promise constructor anti-pattern, which combines nesting with redundant use of the promise constructor to wrap code that already uses promises.

Second error is unnecessarily nested to achieve a first error. Nesting also limits the scope of the internal error handler, if is unexpected, can lead to errors uncaught. Wherein the variant is a constructor antipatterns Promise, which combines the use of redundant and nested Promise constructor.

 

The third mistake is forgetting to terminate chains with catch. Unterminated promise chains lead to uncaught promise rejections in most browsers.

The third mistake is to forget to catch termination chain. This results in most browsers can not terminate the Promise chain in the rejection.

 

A good rule-of-thumb is to always either return or terminate promise chains, and as soon as you get a new promise, return it immediately, to flatten things:

A good rule of thumb is always to return or terminate Promise chain, and once you get a new Promise, return it. The following is planarized revised code:

doSomething()
.then(function(result) {
  return doSomethingElse(result);
})
.then(newResult => doThirdThing(newResult))
.then(() => doFourthThing())
.catch(error => console.error(error));

 

Note that () => x is short for () => { return x; }.

Note :() => x is () => {return x;} shorthand.

Now we have a single deterministic chain with proper error handling.

The code is written with the appropriate wording clear and simple error handling chain.

Using async/await addresses most, if not all of these problems—the tradeoff being that the most common mistake with that syntax is forgetting the await keyword.

Use async / await can solve most of the above error when using async / await, the most common mistake is to forget await keyword.

 

When promises and tasks collide

If you run into situations in which you have promises and tasks (such as events or callbacks) which are firing in unpredictable orders, it's possible you may benefit from using a microtask to check status or balance out your promises when promises are created conditionally.

If you think microtasks may help solve this problem, see the microtask guide to learn more about how to use queueMicrotask() to enqueue a function as a microtask.

 

Guess you like

Origin www.cnblogs.com/duasonir/p/12657338.html
Recommended