Understanding and use of Promise and Async/await

 

Understanding and use of Promise and Async/await

 

 

Note: there are many codes in the text, which may be difficult to browse on the mobile phone. It is recommended to browse on the PC. if the code layout is disordered, please click the more connection at the end of the text to view the original text with more friendly layout.

1, Pre knowledge

1.1 distinguish between instance objects and function objects

Instance object: the object generated by the new function is called instance object, or object for short.

Function object: when a function is used as an object, it is called function object for short

function Fn() {}
const fn = new Fn() // fn is an instance object
Fn.bind({}) // Fn is the function object

 

1.2 two types of callback functions

Synchronous callback

  • Understanding: execute immediately, complete execution is finished, and will not be put into the callback queue
  • Example: array traversal related callback function / Promise excutor function

Asynchronous callback

  • Understanding: it will not be executed immediately, but will be put into the callback queue for future execution
  • Example: timer callback / ajax callback / Promise successful | failed callback
const arr = [1, 2, 3]
arr.forEach(item => console.log(item)) // Synchronous callback will not be put into the callback queue, but will be executed immediately
console.log('forEatch()after')
setTimeout(() => { // Asynchronous callback will be put into the callback queue, and can be executed only after all synchronous executions
console.log('timout Callback')
}, 0)
console.log('setTimeout after')

 

1.3 JS error handling

Wrong type

  • Error: the parent type of all errors
  • ReferenceError: the referenced variable does not exist
  • TypeError: error with incorrect data type
  • RangeError: the data value is not within its allowed range
  • Syntax error: syntax error
console.log(a) // ReferenceError: a is not defined
let b = null
console.log(b.xxx) // TypeError: Cannot read property 'xxx' of null
function fn() {
fn()
}
fn() // RangeError: Maximum call stack size exceeded
let c = """" // SyntaxError: Unexpected string

Error handling:

  • Capture error: try catch
  • Throw error: throw error

Structure of error object

  • message attribute: error related information
  • Stack attribute: function call stack record information

 

 

2, What is Promise?

2.1 understanding

Abstract expression: Promise is a new solution for asynchronous programming in JS (who is the old? = > in the form of pure callback)

Specific expression:

  • Syntactically, Promise is a constructor
  • Functionally speaking, Promise object is used to encapsulate an asynchronous operation and obtain its results

 

2.2 Promise status change

  1. Change to pending
  2. pending becomes rejected

Note: there are only two kinds, and a Promise object can only be changed once. Whether it becomes success or failure, there will be a result data. The successful result data is generally called value, and the failed result data is generally called reason.

 

2.3 Promise basic process

Promise basic process

1.4 basic use of promise

For example, if the current time is an even number, it means success, otherwise it means failure

// 1. Create a new Promise object
const p = new Promise((resolve, reject) => { // Actuator function, synchronous execution
// 2. Perform asynchronous operation tasks
setTimeout(() => {
const time = Date.now() // If the current time is even, it means success, otherwise it means failure
// 3.1 if successful, call resolve(value)
if (time % 2 === 0) {
resolve('Successful data, value = ' + time)
} else {
// 3.2 if it fails, call reject(reason)
reject('Failed data, reason = ' + time)
}

}, 1000);
})

p.then(value => {
// Accept the successful value data, technical term: onResolved
console.log('Successful callback', value)
}, reason => {
// Accept the failed reason data, technical term: onRejected
console.log('Failed callback', reason)
})

 

3, Why Promise?

3.1 the method of specifying callback function is more flexible

Old: callback function must be specified before starting asynchronous task

// Successful callback function
function successCallback(result) {
console.log('Successful processing:' + result)
}
function failureCallback(error) {
console.log('Processing failed:' + error)
}

// Using pure callback functions
createAudioFileSync(audioSettings, successCallback, failureCallback)

Promise: start asynchronous task = > return promise object = > bind callback function to promise object, and even specify multiple functions after the end of asynchronous task

// Using Promise
const promise = createAudioFileSync(audioSettings)
setTimeout(() => {
promise.then(successCallback, failureCallback)
}, 3000);

 

3.2 support chain call, which can solve the callback problem

What is hell? The callback function is nested. The result of asynchronous execution of the external callback function is that the nested execution conditions are returned, and the code is extended horizontally to the right

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

Disadvantages of callback Hell: it is inconvenient to read and to handle exceptions

Solution: Promise chain call, code level down expansion

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 ultimate solution: async/await, which uses synchronous writing to handle asynchronous operations

async function request() {
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)
}
}

 

4, API description of Promise

1) Promise constructor promise (executor) {}, the executor will immediately synchronize the callback within promise, and the asynchronous operation will be executed in the executor

  1. excutor function: actuator (resolve, reject) = > {}
  2. resolve function: the function we call when the internal definition is successful. Value = > {}
  3. reject function: the function we call when the internal definition fails reason = > {}

2)Promise.prototype.then method: (onresolved, onrejected) = > {}, specify the success callback used to get the success value and the failure callback used to get the failure reason, and return a new promise object

  1. onResolved function: successful callback function (value) = > {}
  2. onRejected function: failed callback function (reason) = > {}

3)Promise.prototype.catch method: (onRejected) = > {}, onRejected function: failed callback function (reason) = > {}, then() syntax sugar, equivalent to: then(undefined, onRejected).

4)Promise.resolve method: (value) = > {}, value: successful data or promise object, and return a successful / failed promise object.

5)Promise.reject method: (reason) = > {}, reason: the reason of failure, and returns a promise object of failure

6)Promise.all method: (promises) = > {}, promises: an array containing n promises. A new promise is returned. Only when all promises are successful can it succeed. As long as one fails, it will fail directly.

7)Promise.race method: (promises) = > {}, promises: an array containing n promises returns a new promise. The result state of the first completed promise is the final result state.

// Generate a Promise object with a success value of 1
const p1 = new Promise((resolve, reject) => {
resolve(1)
})
// Generate a Promise object with a success value of 2
const p2 = Promise.resolve(2)
// Generate a Promise object with a failure value of 3
const p3 = Promise.reject(3)

p1.then(value => console.log(value))
p2.then(value => console.log(value))
p3.catch(reason => console.error(reason))

// const pAll = Promise.all([p1, p2])
const pAll = Promise.all([p1, p2, p3])
pAll.then(values => {
console.log('all onResolved()', values) // all onResolved() [ 1, 2 ]
}, reason => {
console.log('all onRejected()', reason) // all onRejected() 3
})

const race = Promise.race([p1, p2, p3])
race.then(value => {
console.log('all onResolved()', value)
}, reason => {
console.log('all onRejected()', reason)
})

 

5, Several key problems of Promise

 

5.1 how to change Promise status

resolve(value). If it is pending, it will become resolved

reject(reason). If it is currently pending, it will become rejected

Throw an exception. If it is currently pending, it will become rejected

const p = new Promise((resolve, reject) => {
// resolve(1) // Promise becomes resolved successfully
// reject(2) // Promise changes to rejected failed state
// Promise changes to rejected failure status, and reason is the error thrown
throw new Error('The exception I threw')
// Change to rejected failure status, and reason is 3 thrown
// throw 3
})
p.then(
value => {},
reason => { console.log('reason :', reason); }
)

 

5.2 when a promise specifies multiple success / failure callback functions, will they be called?

Called when promise changes to the corresponding state

const p = new Promise((resolve, reject) => {
// Change to rejected failure status, and reason is 3 thrown
throw 3
})
p.then(
value => {},
reason => { console.log('reason :', reason); }
)
p.then(
value => {},
reason => { console.log('reason2 :', reason); }
)
// result:
// reason : 3
// reason2 : 3

 

5.3 who changes promise state and specifies callback function first?

It is possible to specify the callback first and then change the state, but you can also change the state first and then specify the callback.

How to change the status before specifying callback?

  • Directly call resolve()/reject() in the executor
  • Call then() after a longer delay

When can I get the data?

  • If you specify the callback first, when the state changes, the callback function will be called to get the data
  • If the state of the is changed first, when the callback is specified, the callback function will be called to get the data
// General: specify the callback function first, and then change the state
new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1) // After changing the state (specifying data at the same time), execute the callback function asynchronously
}, 1000);
}).then( // Specify the callback function first and save the currently specified callback function
value => {},
reason => { console.log('reason :', reason); }
)

// Change the status first, and then specify the callback function
new Promise((resolve, reject) => {
resolve(1) // Change the status first (specify data at the same time)
}).then( // After the callback function is specified, the callback function is executed asynchronously
value => { console.log('value2: ', value);},
reason => { console.log('reason2 :', reason); }
)

const p = new Promise((resolve, reject) => {
resolve(1) // Change the status first (specify data at the same time)
})
setTimeout(() => {
p.then(
value => { console.log('value3: ', value);},
reason => { console.log('reason3 :', reason); }
)
}, 1500);

 

5.4 promise. What determines the result status of the new promise returned by then()?

Simple expression: determined by the execution result of the callback function specified by then().

Detailed expression:

  • If an exception is thrown, the new promise becomes rejected and reason is the exception thrown
  • If any value other than promise is returned, the new promise becomes resolved and value is the returned value
  • If another new promise is returned, the result of this promise will become the result of the new promise
new Promise((resolve, reject) => {
resolve(1)
}).then(
value => {
console.log('onResolved1()', value); // 1
// return 1.1 or
return Promise.resolve(1.1)
// return Promise.reject(1.1)
// throw 1.1
},
reason => {
console.log('onRejected1()', reason);
}
).then(
value => { console.log('onResolved2()', value); }, // 1.1
reason => { console.log('onRejected2()', reason) } // 1.1
)

 

5.5 how promise connects multiple operation tasks in series

then() of promise returns a new promise, which can be opened into a chain call of then(), and multiple synchronous / asynchronous tasks can be connected in series through the chain call of then().

 

5.6 promise abnormal transmission

When using promise's then chain call, you can specify the failed callback at the end. Any exception in the previous operation will be transferred to the last failed callback for processing.

The following example code demonstrates exception transmission

new Promise((resolve, reject) => {
// resolve(1)
reject(1)
}).then(
value => {
console.log('onResolved1()', value);
return 2
}
).then(
value => {
console.log('onResolved2()', value);
return 3
}
).then(
value => {
console.log('onResolved3()', value);
}
).catch(
reason => {
console.log('onRejected()', reason); // onRejected() 1
}
)

The code will execute The code in catch, but in fact, the execution of the code does not jump directly to catch when it is executed to line 3, but executes one by one from the first then call to the next (passed level by level), but because we don't handle exceptions in then. Not writing an exception handling in then is actually equivalent to adding reason = > {throw reason} or reason = > promise by default reject(reason):

new Promise((resolve, reject) => {
reject(1)
}).then(
value => { console.log('onResolved1()', value); },
// reason => { throw reason }
// perhaps
reason => Promise.reject(reason)
)

Schematic diagram of abnormal transmission of Promise

 

5.7 interrupt promise chain

When the then chain call of promise is used, it is interrupted in the middle and the subsequent callback function is no longer called

Method: return a promise object in pending status in the callback function

new Promise((resolve, reject) => {
resolve(1)
}).then(
value => {
console.log('onResolved1()', value);
return new Promise(() => {}) // Return a pending promise and interrupt the promise chain
}
).then( // This then will not be able to execute
value => { console.log('onResolved2()', value); }
)

 

6, async and await

 

Async/await is actually just a sugar coated syntax sugar based on promises. Like promises, Async/await is non blocking. Async/await makes asynchronous code more synchronous, which is also its advantage.

async function is used to define an asynchronous function that returns an AsyncFunction object. Asynchronous function refers to the function executed asynchronously through the event loop. It will return its result through an implicit Promise,. If you use asynchronous functions in your code, you will find that its syntax and structure will be more like standard synchronous functions.

The await operator is used to wait for a Promise object. It can only be used in asynchronous function async function.

 

6.1 async function

The return value of async function is Promise object. The Promise result returned by async function is determined by the result of function execution

async function fn1() {
return 1
}
const result = fn1()
console.log(result) // Promise { 1 }

The following information can be seen on the console

 

Since it is a Promise object, we use then to call and throw an error, execute onRejected() and reason is the error message is "I am an error"

async function fn1() {
// return 1
// return Promise.resolve(1)
// return Promise.reject(2)
throw 'I was wrong'
}
fn1().then(
value => { console.log('onResolved()', value) },
reason => { console.log('onRejected()', reason) } // onRejected() I was wrong
)

 

6.2 await expression

The expression on the right side of await is generally promise object, but it can also be other values

  1. If the expression is a promise object, await returns the value of promise success
  2. If the expression is another value, this value is directly used as the return value of await
function fn2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1000)
}, 1000);
})
}
function fn4() { return 6 }
async function fn3() {
// Const value = await fn2() / / the expression on the right side of await is Promise, and the result is the value of Promise success
// const value = await 'this is OK'
const value = await fn4()
console.log('value', value)
}
fn3() // value 6

Await must be written in the async function, but there can be no await in the async function. If the Promise of await fails, an exception will be thrown. Try Catch capture processing.

function fn2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
// resolve(1000)
reject(1000)
}, 1000);
})
}
async function fn3() {
try {
const value = await fn2()
} catch (error) {
console.log('Get the result of failure', error)
}
}
fn3() // Get the result of failure 1000

 

6.3 Async/await performs better than Promise

Simple and clean, how many lines of code can be saved by using async/await

For error handling, async/wait can handle synchronous and asynchronous errors with the same structure and easy-to-use classic try/catch, and the error stack can point out the functions containing errors.

Debugging, a great advantage of async/await is that it is easier to debug. Using async/await does not need too many arrow functions, and can be called directly across await like normal synchronous calls.

 

End of the full text

 
 
 
 
 
forward
@Hangzhou programmer Xiao Zhang:
Understanding and use of Promise and Async/await

0 / 2000 words

 

Posted by ralba1998 on Sun, 22 May 2022 23:35:29 +0300