Promise object, a new feature of ES6, although it appeared for a long time, but I believe that there are a lot of small partners or not how to use it, today let us have a good study of it.

The birth of the Promise

1. The meaning of Promise

A Promise is simply a container that holds the result of an event (usually an asynchronous operation) that will end in the future. Syntactically, a Promise is an object from which to get messages for asynchronous operations.

In daily development, often need to use Ajax request data, get the data, and then some processing, to complete some complex interaction effects.

Suppose you need to make multiple requests using Ajax, and each request relies on the data returned from the previous request as its parameters, and then you continue to make requests like this: scenario restore:

$.ajax({
    success:function(res1){
        $.ajax({
            success:function(res2){
                $.ajax({
                    success:function(res3){}}); }}); }});Copy the code

There might be more nesting, so layer by layer execution would definitely consume more wait time, and there would be callback hell if multiple requests were sequential. ES6 figured out a way to fix this, and that’s when promises were born.

So we know that Promise is a solution to asynchronous programming, more reasonable and powerful than traditional callback functions and events.

Promise objects have the following two characteristics:

  • The status of an object is not affected.PromiseThe object represents an asynchronous operation with three states:pending(In progress),fulfilled(Successfully) andrejected(Failed). Only the result of an asynchronous operation can determine the current state, and no other operation can change the state. This is alsoPromiseThe origin of the name.
  • Once the state changes, it never changes again, and you can get this result at any time.PromiseThe state of an object can change in only two ways: frompendingintofulfilledAnd from thependingintorejected. As long as these two things happen, the state is frozen, it’s not going to change, it’s going to stay the same, and that’s calledresolved(Finalized). If the change has already happened, then you are rightPromiseObject to add the callback function, also immediately get this result.

With the Promise object, you can express asynchronous operations as a flow of synchronous operations, avoiding layers of nested callback hell.

There are pros and cons, and promises have some downsides. 1. First, there is no way to cancel a Promise. Once a Promise is created, it will be executed immediately. 2. Second, if you don’t set a callback function, errors thrown internally by a Promise won’t be reflected externally. 3. When in the pending state, there is no way to know what stage of progress is currently in (just started or nearly completed).

Basic use of Promise

2. Basic use of Promise

Here’s a look at its basic usage:

const promise = new Promise(function(resolve, reject) {});Copy the code

The Promise object is a global object, or you can think of it as a class, and when you create a Promise instance, you have to have that new keyword. The Promise constructor takes a function as an argument, resolve and reject, both of which are methods. The resolve method is used to process tasks that change from Pending to Resolved after an asynchronous operation succeeds. The Reject method is used to operate services that fail asynchronously (that is, change from Pending to Rejected). Called when an asynchronous operation fails and passes an error reported by the asynchronous operation as a parameter.

Three states of Promise

As mentioned above, Promise objects have three states:

  • 1.pending: Just created onePromiseInstance, indicating the initial state;
  • 2,fulfilled:resolveWhen the method is called, the operation succeeds.
  • 3,rejected:rejectWhen a method is called, the operation fails.

    The following code creates onePromiseInstance.

const promise = new Promise(function(resolve, reject) {
  // Instantiated state: pending

  if (/* Asynchronous operation succeeded */){
    resolve(value);
    // The resolve method is called, and the status is: depressing
  } else {
    reject(error);
    // reject calls the reject method. The state is rejected}});Copy the code

After the instance is initialized, the state of the object becomes pending; When the resolve method is called, the state will change from pending to success depressing. When reject is called, the state changes from Pending to Failed.

After the Promise instance is generated, you can use the then() method to specify the resolved state and Rejected state callback functions, respectively, to bind the handler after processing the action.

promise.then(function(value) {
  // The handler that operated successfully
}, function(error) {
  // The handler for the failed operation
});
Copy the code

The then() method can take two callback functions as arguments. The first callback is called when the Promise object’s state changes to Resolved. The second callback is called when the Promise object’s state changes to Rejected. The second function is optional and does not have to be provided. Both of these functions accept as arguments a value passed from the Promise object.

In simple terms, arguments are two functions, the first to handle the successful operation and the second to handle the abnormal operation.

Take a simple example of a Promise object.

function timeout(ms) {
  return new Promise((resolve, reject) = > {
    setTimeout(resolve, ms, 'hello world');
  });
}

timeout(100).then((value) = > {
  console.log(value); // hello world
});
Copy the code

In the code above, the timeout() method returns an instance of a Promise that will not happen until some time later. When the state of the Promise instance changes to Resolved after the specified time (the MS argument), the callback function bound to the then() method is triggered.

Promise

let promise = new Promise(function(resolve, reject) {
  console.log('Promise');
  resolve();
});

promise.then(function() {
  console.log('resolved');
});

console.log('Hi! ');

// Promise
// Hi!
// resolved
Copy the code

A Promise is executed immediately after it is created, so the Promise is printed first. Then, the callback specified by the then() method will not execute until all synchronization tasks in the current script have finished, so resolved will output.

Catach () method

For programs that operate on exceptions, Promise provides a single instance method to handle: the catch() method.

Catch () takes a single argument and is used to handle business after an operation exception.

getJSON('/posts.json').then(function(posts) {
  // The processing succeeded
}).catch(function(error) {
  // Handle getJSON and errors that occurred while the previous callback function was running
  console.log('Error! ', error);
});
Copy the code

Promise uses chained calls because both the THEN and catch methods return a Promise object when called.

In the code above, the getJSON() method returns a Promise object, and if the state becomes resolved, the callback specified by the then() method will be called; If an asynchronous operation throws an error, the status changes to Rejected, and the callback specified by the catch() method is called to handle the error. In addition, callbacks specified by the then() method are also caught by the catch() method if an error is thrown during execution.

const promise = new Promise(function(resolve, reject) {
  throw new Error('test');
});
promise.catch(function(error) {
  console.log(error);
});
// Error: test
Copy the code

In the code above, a promise throws an error that is caught by the callback specified by the catch method.

If the Promise state has changed to Resolved, throwing an error is invalid.

const promise = new Promise(function(resolve, reject) {
  resolve('ok');
  throw new Error('test');
});
promise
  .then(function(value) { console.log(value) })
  .catch(function(error) { console.log(error) });
// ok
Copy the code

In the preceding code, a Promise is not caught if it is raised after the resolve statement. Because once a Promise state changes, it stays that state forever.

Because the promise.prototype. then and promise.prototype. catch methods return a Promise object, they can be called chained

Note: If a Promise object is in the fulfilled or Rejected state instead of pending, it can also be called Texas state. You might also hear the term resolved, which means that a promise object is in a settled state.

So many chestnuts, if not understand, let us string a string, comb the above mentioned. (Read the notes carefully)

// First create an instance of 'Promise' with the new keyword
const promise = new Promise(function(resolve, reject){
    // Assume the value of state is true
    let state = true;
    if( state ){
        // Call the operation success method
        resolve('Operation successful');
        // State: From pending to depressing
    }else{
        // Call the operation exception method
        reject('Operation failed');
        // State: From pending to Rejected}}); promise.then(function (res) {
    // The handler that operated successfully
    console.log(res)
}).catch(function (error) {
    // The handler for the failed operation
    console.log(error)
})
// Console output: Operation successful
Copy the code

The above example demonstrates the use of the then and catch methods from instance creation, state transitions, and so on.

What do we do with promises if there are layers of dependencies between operations?

const promise = new Promise(function(resolve, reject){
  if( true) {// Call the operation success method
    resolve('Operation successful');
    // State: From pending to depressing
  }else{
    // Call the operation exception method
    reject('Operation failed');
    // State: From pending to Rejected}}); promise.then(a) .then(b) .then(c) .catch(requestError);function a() {
    console.log('Request A successful');
    return 'Request B, who's next?';
}
function b(res) {
    console.log('Result of previous step:'+ res);
    console.log('Request B successful');
    return 'Request C, who's next?';
}
function c(res) {
    console.log('Result of previous step:'+ res);
    console.log('Request C successful');
}
function requestError() {
    console.log('Request failed');
}
Copy the code

As shown in the figure:

The above code creates an instance and declares four functions, three of which represent request A, request B, and request C. With the THEN method, three request operations no longer need to be nested in layers. We use the then method to bind the three operations in the order in which they are called. Moreover, if request B depends on the result of request A, we can use the return statement to pass the required data as parameters to the next request. In the example, we use the return implementation to pass parameters to the next operation.

To get a more intuitive view of what the example shows, here is a diagram:

Here’s another example of the Promise’s microtask order:

var p = new Promise( (resolve, reject) = > {
    setTimeout( (a)= > {
        console.log('1');
    }, 3000);
    resolve(1);
}).then( (a)= > {  Then () 1-1
    Promise.resolve().then( (a)= > { Then () 2-1
        Promise.resolve().then( (a)= > { // description:.then() 3-1
            console.log('2');
        })
    })
}).then( (a)= > { Then () 1-2
    console.log('3');
})
/ / 3
/ / 2
// 1 (execute printed value after 3 seconds)
Copy the code

The above example explains:

  • 1. The first is carried outnew PromiseThe first layer of code is encounteredsetTimeoutAnd push it intoMacro taskQueue (not executed at this time, after the macro task of the current script block), and then encounteredresolve, the implementation ofPromiseThe following code.
  • Encountered 2..then 1-1To pushMicro tasksQueue (just pushed, not executed, so.then 1-2If no other operations need to be processed (such as pushing other microtasks to the queue), the function in the current microtask queue will be executed.then 1-1The callback function of.
  • 3. Perform.then 1-1A complete state is found in the callback functionPromiseObject. Don’t worry about it. Keep going. Got it.then 2-1To pushMicro tasksQueue (just pushed, not executed), at this point.then 1-1The callback completes (there is no return value, which is equivalent to returning a undefined value), andPromiseTo move on and meet.then 1-2Continue pushingMicro tasksQueue (still not executed), then finds no other operation, starts to execute the function in the current microtask queue (now stored in the microtask queue).then 2-1and.then 1-2To execute the callback function.then 2-1A completed state is encountered when the callback function ofPromiseDon’t worry about it. keep going. there you go.then 3-1And push it intoMicro tasksQueue (not executed) and then execute.then1-2The callback to print3At this point, there are no other operations, so continueMicro tasksThe rest of the function in the queue, that is.then 3-1The callback function to print2
  • 4. At this point, the micro task queue is complete, and the next macro task in the macro task queue is executed1

Here’s another example of the microtask order chestnut 2 from Promise:

var p2 = new Promise( (resolve, reject) = > {
    setTimeout( (a)= > {
      console.log('1');  
    }, 3000)
    resolve(1);
}).then( (a)= > { Then () 1-1
    Promise.resolve().then( (a)= > { Then () 2-1
        console.log('2');
    }).then( (a)= > { Then () 1-2
        console.log('3'); })})/ / 2
/ / 3
// 1 (execute printed value after 3 seconds)
Copy the code

The above example explains:

  • 1. Like chestnuts 1.
  • Like chestnuts 2.
  • 3. Perform.then 1-1A complete state is found in the callback functionPromiseObject. Don’t worry about it. Keep going. Got it.then 2-1, pushed into the microtask queue (just pushed, not executed).then 1-1Callback completed (NonereturnValue, equivalent toreturnaundefined), and thenPromiseTo move on and meet.then 1-2, continues to push into the microtask queue (still not executed), then finds no other operations, starts to execute the current microtask queue function (now stored in the microtask queue).then 2-1and.then 1-2To execute the callback function.then 2-1The callback function to print2And then execute.then1-2The callback to print3.
  • 4. At this point, the micro task queue is complete, and the next macro task in the macro task queue is executed1

In addition to providing instance methods, Promise also provides class methods that can be called without creating an instance. Here are a few examples:

Promise. All () method

The promise.all (iterable) method returns an instance of a Promise that is resolved when all promises in iterable arguments are “resolved” or when the arguments do not contain the Promise; If a promise fails (Rejected), the instance calls back (reject) because of the result of the first failed promise.

var promise1 = Promise.resolve(3);
var promise2 = 42;
var promise3 = new Promise(function(resolve, reject) {
  setTimeout(resolve, 3000.'foo');
});

Promise.all([promise1, promise2, promise3]).then(values= > {
  console.log(values);
});
// expected output: Array [3, 42, "foo"]
Copy the code

Resolution:

Because instance promise3 has not yet entered the successful depressing state; This will be a big pity 3000 ms later, when promise.all () will enter the then method and output: [3, 42, “foo”]

Application scenario: An operation needs to be supported by multiple interface requests, but these interface requests do not depend on each other and do not need to be nested layer by layer. In this case, the promise.all () method is appropriate because it will not operate until all interfaces have been requested successfully.

Note: If a promise is passed in with a failed promise, promise. all asynchronously gives the failed result to the failed state callback, regardless of whether the other promise is completed.

Promise. Finally () method

The finally method is used to specify actions that will be performed regardless of the final state of the Promise object. This method was introduced as a standard in ES2018. This avoids the need to write the same statement once in both then() and catch().

promise
.then(result= >{...}). The catch (error= >{...}). Finally,(a)= > {···});
Copy the code

In the code above, regardless of the last state of the promise, the callback specified by the finally method is executed after the callback specified by then or catch.

Here is an example where the server uses Promise to handle the request and then uses the finally method to shut down the server.

server.listen(port)
  .then(function () {
    // ...
  })
  .finally(server.stop);
Copy the code

The finally() method can be useful if you want to do some processing or cleanup after a promise has been executed regardless of the outcome.

Promise. Race () method

The promise.race () method again wraps multiple Promise instances into a new Promise instance.

const p = Promise.race([p1, p2, p3]);
Copy the code

In the above code, the state of P changes as long as one of the first instances of P1, P2, and P3 changes state. The return value of the first changed Promise instance is passed to p’s callback.

The parameters of the promise.race method are the same as those of the promise.all method. If it is not a Promise instance, the promise.resolve method, described below, is first called to convert the parameters to a Promise instance, and then further processing.

const p = Promise.race([
  fetch('index.php'),
  new Promise(function (resolve, reject) {
    setTimeout((a)= > reject(new Error('request timeout')), 5000)})]); p .then(console.log)
.catch(console.error);
Copy the code

In the code above, if the fetch method fails to return a result within 5 seconds, the status of the variable P changes to Rejected, which triggers the callback specified by the catch method.


let promise1 = new Promise(function(resolve){
    setTimeout(function () {
        resolve('Promise1 instance successful');
    },4000);
});
let promise2 = new Promise(function(resolve,reject){
    setTimeout(function () {
        reject('Promise2 instance fails');
    },2000);
});
Promise.race([promise1, promise2]).then(function(result){
    console.log(result);
}).catch(function(error){
    console.log(error);
});
// Expected Output: Promise2 instance fails
Copy the code

As the reject method is executed after 2000 ms in the promise2 instance, which is earlier than 5000 ms in the promise1 instance, the final output is “Promise2 instance failed”.

Promise. Resolve () method

Sometimes you need to turn an existing object into a Promise object, and the promise.resolve method does this.

const jsPromise = Promise.resolve($.ajax('/whatever.json'));
Copy the code

The code above converts the jquery-generated Deferred object into a new Promise object.

Promise. Resolve is equivalent to the following

Promise.resolve('foo')
/ / equivalent to the
new Promise(resolve= > resolve('foo'))
Copy the code

The promise.resolve (value) method returns a Promise object resolved with the given value. If the value is promise, return the promise; If the value is thenable (that is, with the “then” method), the returned promise “follows “the thenable object and adopts its final state; Otherwise the returned promise will be fulfilled with this value. This function flattens out the multiple layers of nesting of promise-like objects.

Warning: Do not call promise.resolve on thenable that resolves to itself. This leads to infinite recursion because it tries to flatten infinitely nested promises. One example is using it with asynchronous pipes in Angular.

Use the static promise.resolve method

Promise.resolve("Success").then(function(value) {
  console.log(value); // "Success"
}, function(value) {
  // will not be called
});
Copy the code

Resolve an array

var p = Promise.resolve([1.2.3]);
p.then(function(v) {
  console.log(v[0]); / / 1
});
Copy the code

Resolve/Resolve/promise

var original = Promise.resolve(33);
var cast = Promise.resolve(original);
cast.then(function(value) {
  console.log('value: ' + value);
});
console.log('original === cast ? ' + (original === cast));

/* * The print order is as follows, there is a difference between synchronous and asynchronous execution * original === cast? true * value: 33 */
Copy the code
Promise. Reject () method

The promise.reject (reason) method returns a Promise object with the reject reason reason argument.

const p = Promise.reject('Wrong');
/ / is equivalent to
const p = new Promise((resolve, reject) = > reject('Wrong'))

p.then(null.function (s) {
  console.log(s)
});
/ / make a mistake
Copy the code

The above code generates an instance P of the Promise object in the rejected state, and the callback is executed immediately.

Note that the arguments to the promise.reject () method are left as reject arguments to subsequent methods. This is inconsistent with the promise.resolve method.

const thenable = {
  then(resolve, reject) {
    reject('Wrong'); }};Promise.reject(thenable)
.catch(e= > {
  console.log(e === thenable)
})
// true
Copy the code

Promise application

We can write the loading of the image as a Promise, and once the loading is complete, the state of the Promise changes.

const preloadImage = function (path) {
  return new Promise(function (resolve, reject) {
    const image = new Image();
    image.onload  = resolve;
    image.onerror = reject;
    image.src = path;
  });
};
Copy the code

A combination of Generator functions and promises

The process is managed using a Generator function, which typically returns a Promise object when an asynchronous operation occurs.

function getFoo () {
  return new Promise(function (resolve, reject){
    resolve('foo');
  });
}

const g = function* () {
  try {
    const foo = yield getFoo();
    console.log(foo);
  } catch (e) {
    console.log(e); }};function run (generator) {
  const it = generator();

  function go(result) {
    if (result.done) return result.value;

    return result.value.then(function (value) {
      return go(it.next(value));
    }, function (error) {
      return go(it.throw(error));
    });
  }

  go(it.next());
}

run(g);
Copy the code

In Generator G of the code above, there is an asynchronous operation getFoo that returns a Promise object. The function run is used to process the Promise object and call the next method.

Most of the projects I’ve written so far are a combination of Generator functions and promises.

This is a bit long, if you don’t have a collection can be bookmarked, later slowly watch.

The following is my public number, follow me, will let you have unexpected harvest ~