es6 series promise, async, await

Posted by cptnwinky on Fri, 11 Feb 2022 09:45:37 +0100

1, promise

1. Introduction

Promise is a solution for asynchronous programming, which is more reasonable and powerful than the traditional solution "callback functions and events".

Advantages of asynchronous operation:

  • Chain operation reduces the difficulty of coding (solving the problem of callback hell)
  • Code readability is significantly enhanced

state

promise objects have only three states

pending (in progress)
Fully completed
rejected (failed)

characteristic:

(1) The state of the object is not affected by the outside world. Only the result of asynchronous operation can determine the current state.
(2) Once the state changes, it will not change again. This result can be obtained at any time (from pending to fully and from pending to rejected).

2. Basic usage

Promise object is a constructor used to generate promise instances

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

Promise constructor accepts a function as an argument. The two parameters of the function are resolve and reject

The resolve function changes the state of the Promise object from incomplete to successful
The reject function changes the state of the Promise object from incomplete to failed

After the Promise instance is generated, you can use the then method to specify the callback functions of resolved state and rejected state respectively.

// The first callback is called when resolve d, and the second callback is called when rejected
promise.then(function(value) {
  // success
}, function(error) {
  // failure
});

promise.then(function(value) {
  // success
}).catch(function(value) {
  // failure
});

Promise will be executed immediately after it is created. Take an example:

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

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

console.log('Hi!');

// Promise
// Hi!
// resolved.

Promise is executed immediately after it is created. Therefore, promise is printed. The callback function of then method will not be executed until all synchronization tasks in the current script are executed, so resolved Final output.

3. Example method

The instance built by Promise has the following methods:

then()
catch()
finally()

then()

then is the callback function when the instance state changes. The first parameter is called when resolve and the second parameter is called when rejected

The then method returns a new promise instance, which can be written in a chain.

getJSON("/posts.json").then(function(json) {
  return json.post;
}).then(function(post) {
  // ...
}, function (err){
  console.log("rejected: ", err);
});

catch()

The catch() method is then(null, rejection) or An alias for then(undefined, rejection), which specifies the callback function when an error occurs

const promise = new Promise(function(resolve, reject) {
  throw new Error('test');
});
promise.catch(function(error) {
  console.log(error);
});
// Error: test
//In the above code, promise throws an error and is caught by the callback function specified by the catch() method. Note that the above writing is equivalent to the following two writing methods.

// Writing method I
const promise = new Promise(function(resolve, reject) {
  try {
    throw new Error('test');
  } catch(e) {
    reject(e);
  }
});
promise.catch(function(error) {
  console.log(error);
});

// Writing method II
const promise = new Promise(function(resolve, reject) {
  reject(new Error('test'));
});
promise.catch(function(error) {
  console.log(error);
});
Comparing the above two ways of writing, we can find that reject()Method is equivalent to throwing an error

matters needing attention:
(1)try...catch cannot catch invalid js code

try {
    ===
} catch(err) {
    // Will not execute
    console.log(err)
}

(2)try...catch cannot catch asynchronous code

try {
    setTimeout(() => {console.log(1)}, 1000)
} catch(err) {
    console.log(err)
}
getJSON('/posts.json').then(function(posts) {
  // ...
}).catch(function(error) {
  // Error occurred when processing getJSON and the previous callback function
  console.log('An error has occurred!', error);
});

The error of Promise object is "bubbling" and will be passed back until it is caught

getJSON('/post/1.json').then(function(post) {
  return getJSON(post.commentURL);
}).then(function(comments) {
  // some code
}).catch(function(error) {
  // Handle the errors generated by the first three promises
});

In general, use the catch method instead of the second parameter of then()

The error thrown by Promise object will not be passed to the outer code, that is, there will be no response

const someAsyncThing = function() {
  return new Promise(function(resolve, reject) {
    // The following line will report an error because x has no statement
    resolve(x + 2);
  });
};

When the browser runs to this line, it will print the error prompt ReferenceError: x is not defined, but it will not exit the process

In the catch() method, you can throw errors again and catch them through the catch method later (write another catch method to catch exceptions).

const someAsyncThing = function() {
  return new Promise(function(resolve, reject) {
    // The following line will report an error because x has no statement
    resolve(x + 2);
  });
};

someAsyncThing().then(function() {
  return someOtherAsyncThing();
}).catch(function(error) {
  console.log('oh no', error);
  // The following line will report an error because y has no statement
  y + 2;
}).then(function() {
  console.log('carry on');
});
// oh no [ReferenceError: x is not defined]

finily()

The finally() method is used to specify the operation that will be performed regardless of the final state of the Promise object

promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});

The finally method always returns the original value.

// The value of resolve is undefined
Promise.resolve(2).then(() => {}, () => {})

// The value of resolve is 2
Promise.resolve(2).finally(() => {})

// The value of reject is undefined
Promise.reject(3).then(() => {}, () => {})

// The value of reject is 3
Promise.reject(3).finally(() => {})

4. Constructor

Promise constructor has the following methods:

all()
race()
allSettled()
resolve()
reject()
try()

all()

The all() method is used to wrap multiple Promise instances into a new Promise instance

const p = Promise.all([p1, p2, p3]);

The state of p is determined by p1, p2 and p3, which can be divided into two cases.

(1) Only when the states of p1, p2 and p3 become fully, the state of p will become fully. At this time, the return values of p1, p2 and p3 form an array and are passed to the callback function of p.

(2) As long as one of p1, p2 and p3 is rejected, the state of p becomes rejected. At this time, the return value of the first rejected instance will be passed to the callback function of p.

The promise instance as a parameter defines the catch method. Promise will not be triggered when rejected occurs Call's catch method. If the catch method is not defined, promise. Is called Catch method of all().

const p1 = new Promise((resolve, reject) => {
  resolve('hello');
})
.then(result => result)
.catch(e => e);

const p2 = new Promise((resolve, reject) => {
  throw new Error('Wrong report');
})
.then(result => result)
.catch(e => e);

Promise.all([p1, p2])
.then(result => console.log(result))
.catch(e => console.log(e));
// ["hello", Error: error reported]

5. Usage scenario

(1) Write the loading of the picture as a Promise. Once the loading is completed, the state of the Promise will change

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

(2) When the next asynchronous request depends on the result of the last request, we can also solve the problem friendly through chain operation
(3) Realize the merging of multiple requests and summarize all request results

2, async

1. Introduction

The return value of async function is Promise object

Furthermore, async function can be regarded as a Promise object wrapped by multiple asynchronous operations, and await command is the syntax sugar of internal then command.

2. Grammar

async function name([param[, param[, ... param]]]) { statements }

Name: function name.
param: the name of the parameter to be passed to the function.
Statements: function body statements.

async is followed by a function and cannot be followed by other characters (such as numbers, strings, etc.)

3. Basic usage

The async function returns a Promise object. You can use the then method to add a callback function.

async function helloAsync(){
    return "helloAsync";
  }
  
console.log(helloAsync())  // Promise {<resolved>: "helloAsync"}
 
helloAsync().then(v=>{
   console.log(v);         // helloAsync
})

There may be await expressions in async function. When async function is executed, if await is encountered, the execution will be suspended first. After the triggered asynchronous operation is completed, the execution of async function will be resumed and the parsed value will be returned.

The await keyword is only valid in async function. If you use await outside the async function, you will only get a syntax error.

function testAwait(){
   return new Promise((resolve) => {
       setTimeout(function(){
          console.log("testAwait");
          resolve();
       }, 1000);
   });
}

// Equivalent to
async function testAwait(){
   await new Promise((resolve) => {
       setTimeout(function(){
          console.log("testAwait");
          resolve();
       }, 1000);
   });
}
 
async function helloAsync(){
   await testAwait();
   console.log("helloAsync");
 }
helloAsync();
// testAwait
// helloAsync

The async function returns a promise with the status value resolved
1. Write return in the async function, and the value of Promise object resolve is undefined
2. The value of rerun is used as the value of resolved

3, await

1. Introduction

await is followed by a function that returns new promise and executes it
await can only be placed in async functions

2. Grammar

[return_value] = await expression;

expression: a Promise object or any value to wait for.

3. Basic usage

The await command is followed by a Promise object that returns the result of the object. If it is not a Promise object, the corresponding value will be returned directly

async function f() {
  // Equivalent to
  // return 123;
  return await 123;
}

f().then(v => console.log(v))
// 123

The await command is followed by a Promise object, which can also be followed by other values, such as strings, Booleans, numeric values and ordinary functions.

function testAwait(){
   console.log("testAwait");
}
async function helloAsync(){
   await testAwait();
   console.log("helloAsync");
}
helloAsync();
// testAwait
// helloAsync

How await handles the following different expressions:

Promise object: await will pause the execution, wait for the promise object to resolve, and then resume the execution of async function and return the resolved value.
Non Promise object: returns the corresponding value directly.

If the Promise object behind any await statement changes to the reject state, the execution of the entire async function will be interrupted.

async function f() {
  await Promise.reject('Error ');
  await Promise.resolve('hello world'); // Will not execute
}

f().then().catch()  // Catch catch exception

If you want the previous asynchronous operation to fail, do not interrupt the subsequent asynchronous operation
1. Use try catch
2. The Promise object behind await is followed by a catch method to deal with the possible errors.

4. Attention

1. For asynchronous operations after multiple await commands, if there is no secondary relationship, it is best to let them trigger at the same time.

let foo = await getFoo();
let bar = await getBar();

// Writing method I
let [foo, bar] = await Promise.all([getFoo(), getBar()]);

// Writing method II
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;

2. The await command can only be used in async functions. If it is used in ordinary functions, an error will be reported

// report errors
async function hh() {
   setTimeout(() => {await b()},1000)
}
// Correct writing
function hh() {
   setTimeout(async () => {await b()},1000) // await follows async
}

4, Thinking questions

console.log('script start')

async function async1() {
  console.log('async1 start')
  await async2()
  console.log('async1 end')
  return 'async then'
}

async function async2() {
  console.log('async2 end')
}
async1()

setTimeout(function() {
  console.log('setTimeout')
}, 0)

async1().then(function (message) { console.log(message) })

new Promise(resolve => {
  console.log('Promise')
  resolve()
})
  .then(function() {
    console.log('promise1')
  })
  .then(function() {
    console.log('promise2')
  })

console.log('script end')

Topics: Javascript Front-end ECMAScript