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

6次阅读

共计 3099 个字符,预计需要花费 8 分钟才能阅读完成。

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 是下一轮事件循环开始时被执行而后输入

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

正文完
 0