Why is JS asynchronous?

javascript is a single-threaded language, that is, only one task can be completed at a time. If there are multiple tasks to be executed, they must be queued to complete the task, and the previous task can be executed before the next task can be executed.
Then if the previous task is not over, the next task will never be executed, or the previous task has been executed for a long time, there is no necessary connection between the two tasks before and after, and time is wasted waiting.
So it needs to be asynchronous.

Asynchronous operations commonly used in development

  • Network request
  • IO Operation readfile readDir
  • Timing function setTimeout setInterval etc.
  • In Node.js there is also process.nextTick() setImmediate()

Traditional asynchronous solutions:

  1. Event subscription/publishing mechanism
  2. Callback
Publish/subscribe mechanism

Simple example

// 订阅
emitter.on('eventName', function(message){
   console.log(message)
})
// 发布
emitter.emit('eventName', 'I am a message')

We can see its application in Node.js

var options = {  
    hostname: '127.0.0.1',  
    port: 10086,  
    path: '/pay/pay_callback?' + content,  
    method: 'GET'  
};  
var req = http.request(options, function (res) {  
    console.log('STATUS: ' + res.statusCode);  
    console.log('HEADERS: ' + JSON.stringify(res.headers));  
    res.setEncoding('utf8');  
    res.on('data', function (chunk) {  
        console.log('BODY: ' + chunk);  
    });
    res.on('end', function() {
    })
});  
req.on('error', function (e) {  
    console.log('problem with request: ' + e.message);  
});  
  
req.end(); 

The above is a process of requesting an interface. As a developer, we only need to pay attention to error、data、end . After subscribing to these events, it will automatically trigger the corresponding event when executing the internal process.

The traditional way of callback function will cause callback hell, similar to this

fs.readFile('some1.json', (err, data) => {
    fs.readFile('some2.json', (err, data) => {
        fs.readFile('some3.json', (err, data) => {
            fs.readFile('some4.json', (err, data) => {

            })
        })
    })
})

Promise circumvents this point and uses the form of chain call, which is more readable

readFilePromise('some1.json').then(data => {
    return readFilePromise('some2.json')
}).then(data => {
    return readFilePromise('some3.json')
}).then(data => {
    return readFilePromise('some4.json')
})

Asynchronous to synchronous

This is a scenario that we often encounter when developing, asynchronous code is executed synchronously.
Write two simulated asynchronous functions

var f1 = function() {
    return new Promise(resolve => {
        setTimeout(() => {
            console.log('f1 is run');
            resolve('f1 done')
        }, 3000)
    })
}
var f2 = function() {
    return new Promise(resolve => {
        setTimeout(() => {
            console.log('f2 is run');
            resolve('f2 done')
        }, 2000)
    })
}
  1. Promise

    f1().then(res => {
     return f2()
    }).then(res => {
    })
    // 输出
    3s后输出:f1 is run
    再过2s后输出:f2 is run

    If there are many asynchronous functions, there will be many then

    f1().then(res => {
     return f2()
    }).then(res => {
     return f3()
    }).then...

    Can be written more concisely

    var arr = [f1, f2] // 待执行promise数组
    var p = Promise.resolve()
    for(let pro of arr) {
     p = p.then(res => pro(res))
    }
  2. reduce
    Actually the same as 1

    var arr = [f1, f2]
    arr.reduce((p,c) => {
     return p.then((res) => c(res))
    }, Promise.resolve())
  3. async await

    var arr = [f1, f2]
    async function doFunc() {
     for(let p of arr) {
         await p()
     }
    }
  4. Generator

    function * gen() {
     yield f1()
     yield f2()
    }
    let g = gen()
    g.next()
    g.next()

The above methods can achieve the result of secondary execution.
1.2.3.4 are all executed and then output. What if it is executed concurrently and then output in order? It is a bit similar to Promise.all , in fact, it is concurrent execution, and then the execution result is stored first, and then output in order. This is more efficient for concurrent execution of two unrelated asynchronous functions.

async function testFunc() {
    var arr = [f1, f2]
    // 并发执行
    var promiseArr = arr.map((fn) => fn())
    for(let result of promiseArr) {
        // 同步返回结果
        console.log('result', await result);
    }
}
// 输出
f2 is run
f1 is run
result f1 done
result f2 done

Promise

For Promise, we have to memorize it according to the main points

    1. Long chain call
    1. Promise not resolve or reject , it will always be in the state pending
    1. If it is in pending , it is impossible to know whether it is just beginning or about to end
    1. Cannot cancel Promise once it is created, it will be executed immediately, and cannot be canceled halfway
    1. If the callback function is not set, Promise will not be reflected to the outside (so it is generally recommended that the promise object be followed by the catch method)
    1. Once the state changes, it will remain in that state forever and will not change anymore
    1. If it is a chain call promise object is consistent with the new object
    1. reject is equivalent to throwing an error
    1. catch method returns the promise object (you can continue to follow then )
    1. Promise object of 0613c24c22d2e9 immediately is resolve() at the end of the current round of "event loop" (event loop) , not at the beginning of the next round of "event loop"

For point 2, give an example:

function pend () {
    return new Promise(resolve => {
        // 没有resolve
        console.log('aaaa');
    })
}
pend().then(() => {
    console.log('bbb');
})
// 输出
aaaa
Promise {<pending>}

bbb will never be printed


For point 9, give an example:

setTimeout(() => {
    console.log('three');
},0)
Promise.resolve().then(() => {
    console.log('two');
})
console.log('one');
// 输出
one
two
three

one is output immediately
two is executed at the end of the "this round" event loop and then output
three is executed at the beginning of the next round of event loop and then output

The writing is a bit general. Follow-up will add more questions. Welcome to discuss together.


高压郭
961 声望494 粉丝

从简单到难 一步一步