JS 异步发展流程(回调函数=>Async/await)
栏目: JavaScript · 发布时间: 5年前
内容简介:异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。 异步执行的运行机制如下:当然回调函数也有它的缺点:
异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。 异步执行的运行机制如下:
- 所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
- 主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
- 一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
- 主线程不断重复上面的第三步。
1、 回调函数
场景: 读取一个文件
let fs = require('fs') fs.readFile('./1.txt', 'utf8', function(err, data){ // 回调的特点是第一个参数一般为错误对象 if (err) { // 如果err有值说明程序出错了 console.log(err) } else { // 否则表示成功获取到数据data console.log(data) } }) 复制代码
当然回调函数也有它的缺点:
- 无法捕获错误(使用try catch)
funnction readFile (fileName) { fs.readFile(fileName, 'utf8', function (data) { if (err) { console.log(err) } else { console.log(data) } }) } try { readFile('./1.txt') } catch (e) { // 如果上边读取文件出错,获取不到错误信息 console.log('err', e) } 复制代码
readFile 方法中无法返回读取到文件的内容(data)
fs.readFile('./data1.txt', 'utf8', function (err, data1) { fs.readFile('./data2.txt', 'utf8', function (err, data2) { fs.readFile('./data3.txt', 'utf8', function (err, data3) { fs.readFile('./data4.txt', 'utf8', function (err, data4) { fs.readFile('./data5.txt', 'utf8', function (err, data5) { console.log(data1, data2, data3, data4, data5) }) }) }) }) }) // 这样的代码称为恶魔金字塔;且有以下问题 // 1、代码非常难看 // 2、难以维护 // 3、效率比较低,因为它们是串行的;一次只能请求一个文件 复制代码
2、事件发布订阅
为了解决回调嵌套的问题
let EventEmitter = require('events') // nodejs核心模块之一,包含两个核心方法: // on >> 表示注册监听,emit >> 表示发射事件 let eve = new EventEmitter() let html = {} // 存放页面模板和数据 eve.on('reading', function (key, value) { html[key] = value if (Object.keys(html).length == 2) { console.log(html) } }) fs.readFile('./template.txt', 'utf8', function (err, template) { eve.emit('reading', template) // 触发reading事件,执行事件的回调函数向html中填入模板 }) fs.readFile('./data.txt', 'utf8', function (err, template) { eve.emit('reading', template) // 触发reading事件,执行事件的回调函数向html中填入数据 }) 复制代码
3、哨兵变量
事件发布订阅已经可以解决回调嵌套的问题,但是还需要引入events模块; 利用哨兵变量一样可以解决回调嵌套的问题,且不需要引入其他模块
// 定义一个哨兵函数来处理 function done (key, value) { html[key] = value if (Object.keys(html).length == 2) { console.log(html) } } fs.readFile('./template.txt', 'utf8', function (err, template) { done('template', template) }) fs.readFile('./data.txt', 'utf8', function (err, template) { done('data', data) }) // 可以封装一个高阶函数去生成哨兵函数 function render (length, cb) { let htm = {} return function (key, value) { html[key] = value if (Object.keys(html).length == length) { cb(html) } } } let done = render(2, function (html) { console.log(html) }) 复制代码
4、Promise
上述方法都是用回调函数来处理异步;我们的目标是把异步往同步的方向靠拢
//promise的用法不再阐述,可自行查阅文档 let promise1 = new Promise(function (resolve, reject) { fs.readFile('./1.txt', 'utf8', function (err, data) { resolve(data) }) }) promise1.then(function (data) { console.log(data) }) 复制代码
5、Generator(生成器)
当我们在调用一个函数的时候,它并不会马上执行,而是需要我们手动的去执行迭代操作(next方法);简单来说,调用生成器函数会返回一个迭代器,可以用迭代器来执行遍历每个中断点(yield) 调用next方法会有返回值value,是生成器函数对外输出的数据;next方法还可以接受参数,是向生成器函数内部输入的数据
- 生成器简单使用
// 方法名前边加*就是生成器函数 function *foo () { var index = 0; while (index < 2) { yield index++; //暂停函数执行,并执行yield后的操作 } } var bar = foo(); // 返回的其实是一个迭代器 console.log(bar.next()); // { value: 0, done: false } console.log(bar.next()); // { value: 1, done: false } console.log(bar.next()); // { value: undefined, done: true } 复制代码
- 生成器 + Promise解决异步的实现
function readFile (filaName) { return new Promise(function (resolve, reject) { fs.readFile(filename, function (err, data) { if (err) { reject(err) } else { resolve(data) } }) } function *read() { let template = yield readFile('./template.txt') let data = yield readFile('./data.txt') return { template: template, data: data } } // 生成迭代器r1 let r1 = read() let templatePromise = r1.next().value templatePromise.then(function(template) { // 将获取到的template的内容传递给生成器函数 let dataPromsie = r1.next(template).value dataPromise.then(function(data) { //最后一次执行next传入data的值;最后返回{template, data} let result = r1.next(data).value console.log(result) }) }) 复制代码
- 生成器 + promise的实现已经有了一些同步的样子;借助一些工具(co),可以优雅的编写上述的代码
//实现 co 方法 //参数是一个生成器函数 function co (genFn) { let r1 = genFn() return new Promise(function(resolev, reject) { !function next(lastVal) { let p1 = r1.next(lastVal) if (p1.done) { resolve(p1.value) } else { p1.value.then(next, reject) } }() }) } //现在获取上边的result可以这样来取 co(read).then(function(result) { console.log(result) }) 复制代码
6、Async/await
Async其实是一个语法糖,它的实现就是将Generator函数和自动执行器(co),包装在一个函数中
//实现 co 方法 //参数是一个生成器函数 async function read() { let template = await readFile('./template.txt'); let data = await readFile('./data.txt'); return template + '+' + data; } // 等同于 function read(){ return co(function*() { let template = yield readFile('./template.txt'); let data = yield readFile('./data.txt'); return template + '+' + data; }); } 复制代码
异步编程发展的目标就是让异步逻辑的代码看起来像同步一样,发展到Async/await,是处理异步编程的一个里程碑。
特此感谢: PandaShen 、 seventhMa
以上所述就是小编给大家介绍的《JS 异步发展流程(回调函数=>Async/await)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 解决异步的方案---回调函数
- javascript异步中的回调
- Netty推荐addListener回调异步执行
- JavaScript异步之从回调函数到Promise
- netty的Future异步回调难理解?手写个带回调异步框架就懂了
- JavaScript 异步编程和回调 – JavaScript 完全手册(2018版)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Perl语言入门 第六版(中文版)
Randal L.Schwartz、brian d foy、Tom Phoenix / 盛春 / 东南大学出版社 / 2012-3 / 62.00元
《Perl语言入门(第6版)(中文版)》根据作者施瓦茨、福瓦、菲尼克斯从1991年开始的教学经验积累汇聚而成,多年来十分畅销。此次第六版涵盖了最新的Perl5.14版本的变化。《Perl语言入门(第6版)(中文版)》每章都包含若干习题,帮助你巩固消化刚学到的知识。也许其他书籍只是想着灌输Perl编程的条条框框,但《Perl语言入门(第6版)(中文版)》不同,我们希望把你培养成一名真正的Perl程序......一起来看看 《Perl语言入门 第六版(中文版)》 这本书的介绍吧!