4

basic concept

The forms of traditional JavaScript asynchronous programming are roughly divided into the following categories.

  • Callback
  • Event monitoring
  • Publish/Subscribe
  • Promise object

asynchronous

The continuous execution of a task is called synchronization. If the task is executed in two steps, after the first step is executed, other tasks are executed, and when you are ready, the second step is executed again. This kind of discontinuous execution is called asynchronous.

Callback

The callback function is to write the task executed in the second step separately in a function, and call this function directly when the task is re-executed. The English name of the callback function is callback , and the literal translation is "recall".

loadData(url, function (data) {
  console.log(data);
});

Note: After the first step of the task is executed, the context of the task is over, so we generally use var that = this this point when the first step is executed, so that it can be used in callbacks.

function Api(url) {
    this.url = url;
    this.request = function () {
          var that = this
        setTimeout(function () {
            console.log('url', that.url)
        }, 1000)
    }
}

var api = new Api('http://127.0.0.1')
api.request() // url http://127.0.0.1

Generator function

In the asynchronous programming solution, ES6 also provides the Generator function. It is actually an ordinary function with unique characteristics

  1. There is an asterisk between function
  2. yield is used inside the function body to define different internal states.

function* statusGenerator() {
  yield 'pending';
  yield 'running';
  return 'end';
}

var st = statusGenerator();

The above code statusGenerator function returns an iterator object. Three states are defined in the function. Call the iterator next method to point to the next state.

st.next() // { value: 'pending', done: false }
st.next() // { value: 'running', done: false }
st.next() // { value: 'end', done: false }

yield expression

yield expression is the pause flag. When the iterator executes next .

  1. When the yield expression is encountered, the execution of the following operations is suspended, and the value of the expression after yield value attribute value of the returned object.
  2. The next time the next method is called, the execution will continue until the next yield expression is encountered.
  3. If no new yield expression is encountered, it will run until the end of the function until the return statement, and return the value of the expression after the value attribute value of the returned object.
  4. If the function does not have the return statement, the value attribute value of undefined .

for...of loop

We can also use for...of to traverse.

function* statusGenerator() {
  yield 'pending';
  yield 'running';
  return 'end';
}

var st = statusGenerator();
for(let v of st){
  console.log(v)// pending running
}

Generator application

Coroutine

coroutine means that multiple threads cooperate with each other to complete asynchronous tasks. It is an asynchronous programming solution for some programming languages, such as the coroutine implementation goroutine . The general flow of the co-program execution is as follows:

  1. The coroutine A starts to execute.
  2. The A halfway through, enters a pause, and the execution right is transferred to the coroutine B .
  3. (After a period of time) B right of execution.
  4. The coroutine A resumes execution.

The coroutine in JavaScript implements the Generator function, which can hand over the execution right of the function (that is, suspend execution) at the specified place ( yield

For example: we implement a countdown function, wait for the countdown after the task is ready, and execute it together.

function* countdown(num, running) {
    do {
        yield num--
    } while (num > 0)
    running()
}

const tasks = []
const ct = countdown(3, function () {
    console.log('start run task')
    for (let task of tasks) {
        task()
    }
})

for (let i = 0; i < 3; i++) {
    tasks.push(function () {
        console.log('task '+ i)
    })
    ct.next()
}

ct.next()

An asynchronous request package

var fetch = require('node-fetch');

function* request(){
  var url = 'xxxx';
  var user = yield fetch(url); // 返回promise对象,data: {'user':'xxxx'}
  console.log(user);
}

var req = request();
var result = req.next();

result.value.then(function(data){
  return data.user
}).then(function(user){
  req.next(user);                        // 将 user信息传到 request()函数,被user变量接收。
});

async function

ES2017 introduced the async and await keywords. Using these keywords, you can write Promise in a more concise way, without the need to chain call promise deliberately.

The function declared by async async function. You can think of async as the syntactic sugar of Generator, because they have the same essence.

Generator writing

const loadData = function (url) {
    return new Promise(function (resolve, reject) {
        resolve(data);
    });
};

const request = function* () {
    const user = yield loadData('https://user');
    const goods = yield loadData('https://goods');
    console.log(user, goods);
};

async writing

const loadData = function (url) {
    return new Promise(function (resolve, reject) {
        resolve(data);
    });
};

const request = async function () {
    const user = await loadData('https://user');
    const goods = await loadData('https://goods');
    console.log(user, goods);
};

Basic usage

async function returns a Promise object. When the function is executed, once it encounters await it will return first, wait until the asynchronous operation is completed, and then execute the following statement in the function body.

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

async function asyncPrint(value, ms) {
  await timeout(ms);
  console.log(value);
}

asyncPrint('hello', 50);

The value returned by the return statement inside the async function will become the parameter of the callback function of the then

async function hello() {
  return 'hello';
}

hello().then(v => console.log(v))
// "hello"

async function throws an error internally, which will cause the returned Promise object to change to the reject state. The thrown error object will be received by the callback function of the catch

async function hello() {
  throw new Error('Error');
}

hello().then(
  v => console.log(v),
  e => console.log( e)
) // //Error: Error

await command

Under normal circumstances, await followed by a Promise object, and the result of the object is returned. If it is not a Promise object, the corresponding value is returned directly.

async function hello() {
  return await 'hello'
}
hello().then(v => console.log(v)) // hello

async function hello() {
  return await Promise.resolve('hello');
}
hello().then(v => console.log(v)) // hello

Error handling

If await is an error in the asynchronous operation following 06179232d4e461, then the Promise object returned by the function async reject .

async function hello() {
  await new Promise(function (resolve, reject) {
    throw new Error('error');
  });
}

hello()
.then(v => console.log(v))
.catch(e => console.log(e))
// Error:error

So it is best to put the await command in the try...catch code block.

async function hello() {
    try {
        await new Promise(function (resolve, reject) {
            throw new Error('error');
        });
    } catch(e) {
        console.log('err:', e) // error
    }
    return await('hello');
}

const  h = hello();
h.then((v) => {console.log(v)}) // hello

summary

This article records some ways in JavaScript asynchronous programming, Generator function and async and await syntax, welcome to leave a message and exchange.


编程码农
452 声望1.4k 粉丝

多年编程老菜鸟👨‍💻🦍