JS为何有异步?

javascript是单线程的语言,即一次只能实现一个工作,若有多个工作要执行,则必须依照队列排队实现工作,前一个工作执行完能力执行下一个工作。
那么如果上一个工作不完结,下一个工作就永远得不到执行,或者上一个工作执行很久,前后两个工作没有什么必然的分割,白白浪费了工夫在期待。
所以须要异步。

开发中罕用的异步操作

  • 网络申请
  • IO操作 readfile readDir
  • 定时函数setTimeout setInterval
  • 在Node.js中 还有process.nextTick() setImmediate()

传统的异步解决方案:

  1. 事件的订阅/公布机制
公布/订阅机制

简略示例

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

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(); 

下面是一个申请接口的过程, 作为开发者,咱们只须要关注error、data、end这些业务事件点上即可,订阅了这些事件,它在执行外部流程会主动去触发相应的事件

传统的回调函数的形式会造成回调天堂,相似这种

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躲避了这一点,应用链式调用的模式,可读性更高

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

异步变同步

这是咱们开发时常常要遇到的场景 异步代码同步执行。
写两个模仿的异步函数

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

    如果异步函数很多的话,会有很多个then

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

    能够用更简洁的写法

    var arr = [f1, f2] // 待执行promise数组var p = Promise.resolve()for(let pro of arr) { p = p.then(res => pro(res))}
  2. reduce
    其实跟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()

以上的形式都能达到继发执行的后果.
1.2.3.4都是继发执行 而后输入。如果是并发执行 而后按程序输入呢? 有点相似Promise.all,其实就是并发执行,而后将执行后果先存起来,再程序输入.这样对于两个无关联的异步函数 并发执行的效率更高

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 runf1 is runresult f1 doneresult f2 done

Promise

对于Promise,咱们要按要点来记忆它

    1. 很长的链式调用
    1. Promise没有被resolvereject, 它将始终处于pending的状态
    1. 如果是在pending的状态 无奈晓得是刚开始还是快要完结
    1. 无奈勾销Promise 一旦建设它就会立刻执行,无奈中途勾销
    1. 如果不设置回调函数,Promise外部抛出的谬误,不会反馈到内部(所以个别倡议promise对象前面要跟着catch办法)
    1. 一旦状态扭转,就永恒放弃该状态,不会再变了
    1. 如果是链式调用 原promise对象的状态跟新对象保持一致
    1. reject的作用 等同于抛出谬误
    1. catch办法返回的也是promise对象(前面能够持续跟then)
    1. 立刻resolve()Promise 对象,是在本轮“事件循环”(event loop)的完结时执行,而不是在下一轮“事件循环”的开始时

对于第2点,举个例子:

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

bbb将永远不会被打印进去


对于第9点,举个例子:

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

one 是立刻输入
two 是在“本轮”事件循环的开端执行而后输入
three 是下一轮事件循环开始时被执行而后输入

写的有点抽象 后续会再补充 有问题欢送一起探讨