关于javascript:总结异步解决方案实践

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 run
f1 is run
result f1 done
result 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');
})
// 输入
aaaa
Promise {<pending>}

bbb将永远不会被打印进去


对于第9点,举个例子:

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

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

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

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理