JavaScript asynchronous from promise to await

Keywords: Javascript

1. Why use it from callback to Promise

When we perform an asynchronous task, we often use a callback function, which is called at the end of the asynchronous task. It has one thing in common that it will be executed in the future.For example, reading files asynchronously in node.js:

fs.readFile('/etc/passwd', (err, data) => {
  if (err) throw err;
  console.log(data);
});

As we wrote an ajax request function:

// Simplified version, ignore details
const request = function(callback){
  let xhr = new XMLHttpRequest();
   // .....
  xhr.onreadystatechange = function(){
    callback(xhr)              
  }
}
//call
request(function(xhr){
  if (xhr.readyState === 4 && xhr.status === 200) {
      // ....
  } else {
      //....
  }  
})

Even a timer task:

const doSomethingLater = function(delay, callback){
    setTimeout(callback, delay*1000)
}
// call
doSomethingLater(1, function(){
  // .....
})

This may seem like a good way to use callbacks, but when there are asynchronous tasks in the callback function, there may be multiple callbacks, which are the problem of callbacks to Hell.Multilayer callbacks reduce code readability and maintainability.

What Promise did for us

Simply put, Promise wraps an asynchronous task as an object, passes the function to be executed after the asynchronous task is completed to the then method, and calls the function through resolve.If the timer task above can be rewritten to:

const doSomethingLater = function(delay){
    return new Promise((resolve)=>{
      setTimeout(()=>{ resolve() }, delay*1000)
    })
}
doSomethingLater(1)
    .then(()=>{
      console.log('Task 1')
    })

If a timed task is executed again, you can write this instead of nesting another layer of callbacks:

doSomethingLater(1)
    .then(() => {
        console.log('Task 1')
        return doSomethingLater(1)
    })
    .then(() => {
        console.log('Task 2')
    })

The role of Promise:

  • Change the processing function after the asynchronous task is completed: pass it to the then method, and support chain calls to avoid layer-by-layer callbacks.
  • Capture errors: Whether code errors or manual reject(), you can use a function to handle them.

2. Promise details that you may not know

Promise is asynchronous code

Even if your code didn't originally operate asynchronously:

Promise.resolve()
    .then(() => {
        console.log(1)
    })
console.log(2)
// 2
// 1

This allows you to look at knowledge about event loops

Another way to write catch
Promise.reject('error')
    .then(() => {
    })
    .catch((err) => {
        console.log(err)
    })
// Equivalent to
Promise.reject('error')
    .then(() => {
    })
    .then(null, (err) => {
        console.log(err)
    })
// perhaps
Promise.reject('error')
    .then(() => {
    }, (err) => {
        console.log(err)
    })

In fact, catch is just a semantically grammatical sugar, and we can also use that directly to handle errors.

The then or catch method always returns a promise object

The first and second parameters of the then method (or the parameters of the catch) are called under different conditions.

Promise.resolve()
    .then(() => {
        return 1
    })
    .then((res) => {
        console.log(res) // 1
    })
    
Promise.resolve()
    .then(() => {
       // Nothing to return
    })
    .then((res) => {
        console.log(res) // Undefined because the function returns undefined by default 
    })
   

If a promise object is returned:

Promise.resolve()
    .then(() => {
        return new Promise((resolve) => {
            resolve(2)
        })
    })
    .then((res) => {
        console.log(res) // 2, depending on the status of the promise object returned
    })

We can wrap so that the final state of a promise object is always successful:
For example:

const task = () => {
    return new Promise((resolve, reject) => {
        // ....
    })
}
task()
    .then((res) => {
        console.log(res)
    })
    .catch((err) => {
        console.log(err)
    })

Instead of calling the task function to catch errors with an extra layer of catch, you can actually wrap it up:

const task = () => {
    return new Promise((resolve, reject) => {
        // ....
    })
        .then((res) => {
            return {
                status: 'success',
                value: res
            }
        })
        .catch((err) => {
            return {
                status: 'fail',
                value: err
            }
        })
}
// Now the call will be more concise!
task()
    .then((result) => {
        console.log(result)
    })

Errors in catches can also be caught later, because catches also return promise objects

Promise.reject('first error')
    .catch((err) => {
        console.log(err)
        throw new Error('second error')
    })
    .then(null, (err) => {
        console.log(err)
    })

3. await: new grammatical sugar

Await makes asynchronous code more like synchronous code and writes more naturally on serial asynchronous calls.Await is followed by a value or promise object, and if it is a value, it is returned directly.If it is a promise object, it accepts the return value of the success of the promise object.Or call an async function after await

const task = async () => {
    return new Promise((resolve, reject) => {
        resolve(1)
    })
}
const handle = async () => {
    let value = await task()
    console.log(value)
}
handle() // 1

Use await to be aware of error capture, because await only cares about success:

const task = async () => {
    return new Promise((resolve, reject) => {
        reject(1)
    })
}
const handle = async () => {
    let value = await task()
    console.log(value)
}
handle()

Writing this way will cause errors, so either the error is caught in the task function or when the task is called, like this:

const task = async () => {
    return new Promise((resolve, reject) => {
        reject(1)
    })
}
const handle = async () => {
    let value = await task().catch((err) => { console.log(err) })
    console.log(value) // undefine, because the error handler does not return a value, you can also return an error message, which you get through value
}

It is important to note that using await also makes your code asynchronous:

const handle = async () => {
    let value = await 1
    console.log(value)
}
handle()
console.log(2)
// 2
// 1

Finish~

Posted by sphinx9999 on Sat, 14 Dec 2019 00:07:40 -0800