What is promise?

In MDN, a promise is defined in one sentence: a promise object is used to represent the final completion (or failure) of an asynchronous operation and its resulting value.

A few key words can be captured from this definition: PROMISE is an object, an asynchronous operation, an end state, and a result value.

Before we really understand what promise is, we have to think about what problems promise is designed to solve.

background

Javascript is a single-threaded language: single-threaded means that if you have multiple tasks that must be queued, the first task will be executed before the next one is executed.

synchronous

A function is synchronous if the caller gets the expected result (the result of the function’s calculation) when it returns the result.

console.log('joseydong'); // 执行后,获得了返回结果

function wait(){
    var time = (new Date()).getTime();//获取当前的unix时间戳
    while((new Date()).getTime() - time < 5000){}
    console.log('你好我的名字是');
}
wait();
console.log('josey');
console.log('说得太慢了');
Copy the code

In this code, wait is a function that takes five seconds, during which the console.log() function below can only wait.

This is the downside of synchronization: if a function is synchronous, even if it takes time to call the function to perform the task, it will wait until the execution result is obtained.

asynchronous

If the caller does not get the expected result when the function returns, but will get it by some means in the future, this is called asynchracy.

When an asynchronous function is executed, the call will return immediately, but not the expected result. Instead of waiting silently, the callback notifies the caller when the result is returned.

Asynchronous implementation

An asynchronous operation triggers a function call at some point in time.

AJAX, for example, is typically asynchronous:

request.onreadystatechange = function () { if (request.readyState === 4) { if (request.status === 200) { return success(request.responseText); } else { return fail(request.status); }}}Copy the code

Writing callback functions SUCCESS (Request.responseText) and fail(Request.status) in an Ajax operation is not maintainable or reusable.

Think of a better way to write it, such as:

var ajax = ajaxGet('url');
ajax.ifSuccess(success)
    .ifFail(fail)
Copy the code

The advantage of this chaining approach is that the Ajax logic is executed uniformly, regardless of how the returned result is processed, and then the success or FAIL functions are called at some point in the future, depending on whether the returned result succeeds or fails.

promise

The objects that such promises will perform in the future are called promise objects.

Promise has various open source implementations, standardized in ES6 and supported directly by browsers.

Ii. About Promises

A promise is an object from which you can get information about asynchronous operations.

It represents an asynchronous operation with three states:

  • Pending: Indicates the initial state
  • Resolved: The operation is successful. Also known as fulfilled
  • Rejected (Failed) : The operation fails

With promises, asynchronous operations can be expressed as a flow of synchronous operations, but it has the following disadvantages:

  • Once created, it is executed immediately and cannot be cancelled
  • If the callback function is not set, errors thrown inside a promise are not reflected outside
  • When in the pending state, there is no way to know whether it has just started or is about to end.

3. Application examples

Create a promise

var promise = new Promise( /* executor */ function(resolve,reject){ //... });Copy the code

The Executor function is executed immediately by the Promise instance, passing the Resolved and Reject functions.

Within executor, promises have the following possible variations:

  • Resolve is called and the promise state changes from pengding to Resolved, indicating that the promise has been resolved successfully
  • Reject is called, and the promise changes from pengding to Rejected, indicating that the promise value cannot be used for subsequent processing and is rejected.

1. If any exception is thrown during execution of an Executor method, the promise is immediately rejected, the reject method is called, and the executor return value is ignored.

2. If a Promise object is in the Resolved or Rejected state, it can also be described as settled.

Handling Promise instances

  • Then: After the Promise instance is generated, you can use the THEN method to specify the resolved and Rejected state callback functions, respectively. Promise. Then (value => {// error => {// error => {// failure => error}); The then method can accept two callback functions as arguments: the first callback is called in the Pending – > Resolved state; The second callback is called in the pending — > Rejected state; The parameter is the return value of reject; Optional.
  • Catch: promise.prototype. Catch is an alias for. Then (null, Rejection) which specifies the callback when an error occurs. promise.then(res => { // … }). Catch (error => {console.log(‘ error: ‘,error); });
  • The extension promise.prototype. then and promise.prototype. catch methods return a Promise object, so it can be called chained. Instead of the original Promise instance, the new Promise instance is returned with the return value of the function.

1. Errors from the Promise object are bubbling and are passed backwards until they are caught. That is, errors must be caught by a catch statement.

Wrap multiple Promise instances into a new Promise instance

  • Promise.all(iterable): Return promises when promises in the iterable parameters have been promised, or when any of the promises in the rejected state. var promise = Promise.all([p1,p2,p3]);

    P1 & p2 && p3 returns resolved = > promise return resolved p1 | | p2 | | p3 any return of the rejected = > promise state becomes rejected, The return value of the first reject instance is passed to p's callback.Copy the code

Iv. Promise best practices

  • Prevent nested THEN because the return of a THEN is still a promise, the inner promise is executed before the outer THEN is executed. At this point, it’s best to expand it, which gives the same result, and makes it much more readable.

    / / -- -- -- -- -- -- -- -- -- -- -- -- bad writing -- -- -- -- -- -- -- -- -- -- -- -- -- new Promise (resolve = > {the console. The log (" Step 1 "); setTimeout(()=> { resolve('100'); },1000) }).then(value => { // value => 100 return new Promise(resolve => { console.log('Step 1-1'); setTimeout(() => { resolve('110'); }, 1000); }) .then(value => { // value => 110 console.log('Step 1-2'); return value; }) .then(value => { // value => 110 console.log('Step 1-3') return value; }) }) .then(value => { console.log(value); // value = 110 console.log('Step 2') })Copy the code
    / / -- -- -- -- -- -- -- -- -- -- -- -- good writing -- -- -- -- -- -- -- -- -- -- -- -- new Promise ((resolve) = > {the console. The log ("Step 1");
    setTimeout(() => {
      resolve("100");
    }, 1000);
    
    })
    
    .then((value) => {
      // value => 100
      return new Promise((resolve) => {
        console.log("Step 1-1");
        setTimeout(() => {
          resolve("110");
        }, 1000);
      });
    })
    .then((value) => {
      // value => 110
      console.log("Step 1-2");
      return value;
    })
    .then((value) => {
      // value => 110
      console.log("Step 1-3");
      return value;
    })
    .then((value) => {
      console.log(value); // value = 110
      console.log("Step 2");
    });
    Copy the code
  • Use.catch() to catch errors

    Typically promises are handled in one of two ways:

    // ------------ bad way to write -------------
    promise.then(function(data) {
        // success
      }, function(err) {   // Handle only errors that occur when promise runs. Unable to handle error in callback
        // error
      });
    
    // ------------ a good way to write this is ------------
    promise.then(res= > {
        // success
    }).catch(err= > {   // Handle errors that occur when promise and the previous callback function run
        // error 
    });
    
    Copy the code

Because the error thrown by a promise is not passed to the outer layer, the error of a successful callback cannot be handled when using the first notation, so the catch method is recommended.

  • Then method, never to return or throw / / — — — — — — — — — — — — bad writing — — — — — — — — — — — — — — — — — – promise. Then, the function () {getUserInfo (userId); }).then(function () {// You might want to use user information in this callback, but you might find that it doesn’t exist}); If you want to use chained THEN, you must return a Promise object.

What is async/await?

Async: async, await: await.

In simple terms async is used to declare that a function is asynchronous and await is used to wait for an asynchronous method to complete.

The syntax says that await can only appear in async functions, which return a Promise object.

Promises typically do not need to wait, so an async function executed without await will execute immediately, returning a promise object and not blocking subsequent statements, just like a normal promise.

Await is waiting for a Promise object/other value.

Since async returns a promise object, await can be used to wait for the return value of an async function. And, it can wait for the result of any expression, so await can be followed by ordinary function calls or direct quantities.

function getSomething() { return "something"; } async function testAsync() { return Promise.resolve("hello async"); } async function test() { const v1 = await getSomething(); // Promise object const v2 = await testAsync(); // Console. log(v1, v2); } test();Copy the code

Await is an operator used to form an expression, and the result of an await expression depends on what it is waiting for.

If the await is not a promise, then the result of the await expression is equal to the await.

If the await is a Promise, the await will block the code waiting for the Resolved state of the Promise and then get the resolve value as a result of the await expression.

1. This is why await must be placed inside async functions: async function calls do not block, all blocks within it are wrapped in a Promise object and executed asynchronously.

A simple comparison between asyCN /await and promise

Do not use the async/await

function takeLongTime() {
  return new Promise(resolve => {
    setTimeout(() => resolve('hahahha'),1000);
  });
}

takeLongTime().then(value => console.log('heihei',value))
Copy the code

Use the async/await

function takeLongTime() {
  return new Promise(resolve => {
    setTimeout(() => resolve('hahahha'),1000);
  });
}

async function test() {
  const value = await takeLongTime();
  console.log(value);
}

test();
Copy the code

The advantage of async/await is processing then chains

Promises address multi-layer callbacks via then chains, but if you need to process then chains made up of multiple promises at the same time, you can further optimize with async/await.

Consider a scenario that is completed in multiple steps, each of which is asynchronous and dependent on the outcome of the previous step.

function takeLongTime(n) {
    return new Promise(resolve => {
        setTimeout(() => resolve(n + 200), n);
    });
}

function step1(n) {
    console.log(`step1 with ${n}`);
    return takeLongTime(n);
}

function step2(n) {
    console.log(`step2 with ${n}`);
    return takeLongTime(n);
}

function step3(n) {
    console.log(`step3 with ${n}`);
    return takeLongTime(n);
}
Copy the code

Use the Promise method to implement these steps:

function doIt() {
    console.time("doIt");
    const time1 = 300;
    step1(time1)
        .then(time2 => step2(time2))
        .then(time3 => step3(time3))
        .then(result => {
            console.log(`result is ${result}`);
            console.timeEnd("doIt");
        });
}

doIt();

// step1 with 300
// step2 with 500
// step3 with 700
// result is 900
// doIt: 1580.487ms
Copy the code

To implement with async/await:

async function doIt() {
    console.time("doIt");
    const time1 = 300;
    const time2 = await step1(time1);
    const time3 = await step2(time2);
    const result = await step3(time3);
    console.log(`result is ${result}`);
    console.timeEnd("doIt");
}

doIt();
Copy the code

1. Advantages of async/await: more concise code.

Modify the code for the above scenario. The following steps require multiple return values:

function step1(n) {
    console.log(`step1 with ${n}`);
    return takeLongTime(n);
}

function step2(m, n) {
    console.log(`step2 with ${m} and ${n}`);
    return takeLongTime(m + n);
}

function step3(k, m, n) {
    console.log(`step3 with ${k}, ${m} and ${n}`);
    return takeLongTime(k + m + n);
}
Copy the code

If you use promise, you’ll find that handling the return value is a bit more cumbersome:

function doIt() {
    console.time("doIt");
    const time1 = 300;
    step1(time1)
        .then(time2 => {
            return step2(time1, time2)
                .then(time3 => [time1, time2, time3]);
        })
        .then(times => {
            const [time1, time2, time3] = times;
            return step3(time1, time2, time3);
        })
        .then(result => {
            console.log(`result is ${result}`);
            console.timeEnd("doIt");
        });
}

doIt();
Copy the code

To process with async/await:

async function doIt() { console.time("doIt"); const time1 = 300; const time2 = await step1(time1); const time3 = await step2(time1, time2); const result = await step3(time1, time2, time3); console.log(`result is ${result}`); console.timeEnd("doIt"); } doIt(); // step1 with 300 // step2 with 800 = 300 + 500 // step3 with 1800 = 300 + 500 + 1000 // result is 2000 // doIt: 2907.387 msCopy the code

2. Advantages of async/await: Solve the problem that promises are too troublesome to pass parameters.

Async/await a try… Catch processes the Rejected state

async function myFunction() { try { await somethingThatReturnsAPromise(); } catch (err) { console.log(err); Another writing}} / / async function myFunction () {await somethingThatReturnsAPromise (). The catch (function (err) {the console. The log (err); }); }Copy the code