异步编程小结

栏目: JavaScript · 发布时间: 5年前

内容简介:在javascript单线程的世界里,没有异步寸步难行。本章节介绍异步编程的发展,从在(javascript)单线程的世界里,如果有多个任务,就必须排队,前面一个完成,再继续后面的任务。就像一个ATM排队取钱似的,前面一个不取完,就不让后面的取。 为了这个问题,javascript提供了2种方式: 同步和异步。 异步就像银行取钱填了单子约了号,等着叫到号,再去做取钱,等待的时间里还可以干点其他的事儿~举个例子:通过api拿到数据,数据里面有图片,图片加载成功渲染,那么代码如下:

在javascript单线程的世界里,没有异步寸步难行。本章节介绍异步编程的发展,从 callback , Eventspromise , generator , async/await .

为什么要使用异步编程

在(javascript)单线程的世界里,如果有多个任务,就必须排队,前面一个完成,再继续后面的任务。就像一个ATM排队取钱似的,前面一个不取完,就不让后面的取。 为了这个问题,javascript提供了2种方式: 同步和异步。 异步就像银行取钱填了单子约了号,等着叫到号,再去做取钱,等待的时间里还可以干点其他的事儿~

异步回调带来的问题

回调地狱 (Callbacks Hell)

举个例子:通过api拿到数据,数据里面有图片,图片加载成功渲染,那么代码如下:

// 伪代码
request(url, (data) => {
    if(data){
        loadImg(data.src, () => {
            render();
        })
    }
})
复制代码

如果有在业务逻辑比较复杂或者NodeJS I/O操作比较频繁的时候,就成了下面这个样子:

doSth1((...args, callback) => {
    doSth2((...args, callback) => {
        doSth3((...args, callback) => {
            doSth4((...args, callback) => {
                doSth5((...args, callback) => {

                })
            })
        })
    })
})
复制代码

这样的 维护性可读性 ,整个人瞬间感觉不好了~

异常处理

try {
    setTimeout(() => {
        throw new Error('unexpected error');
    }, 100);
} catch (e) {
    console.log('error2', e.message);
}
复制代码

以上代码运行抛出异常,但try catch不能得到未来时间段的异常。

流程控制不方便

流程控制只能通过维护各种 状态 来处理,不利于管理

异步编程现有解决方案对比

事件机制

不管浏览器还是NodeJS,提供了大量内置事件API来处理异步。

事件监听

浏览器中如: websocket , ajax , canvas , imgFileReader 等 NodeJS如: stream , http

自定义事件(本质上是一种发布订阅模式)

  • NodeJS中的 EventEmitter 事件模型
  • 浏览器中:如DOM可使用 addEventListener ,此外浏览器也提供一些自定义事件的API,但兼容性不好,具体可以这篇文章;也可以用Node中的 EventEmitter ; jquery 中也对此做了封装, on , bind 等方法支持自定义事件。

事件小结

事件一定程度上解决了 解耦 和提升了代码 可维护性 ;对于 异常处理 ,只有部分支持类似 error事件 才能处理。若想实现异常处理机制,只有自己模拟error事件,比较繁琐。

Promise

Promise严格来说不是一种新技术,它只是一种机制,一种代码结构和流程,用于管理异步回调。为了统一规范产生一个Promise/A+规范,点击查看Promise/A+中文版,cnode的 William17 实现了Promise/A+规范,有兴趣的可以点这里查看

  • promise 状态由内部控制,外部不可变
  • 状态只能从 pendingresovled , rejected ,一旦进行完成不可逆
  • 每次 then/catch 操作返回一个promise实例,可以进行链式操作
异步编程小结

部分代码如下:

readFile(path1).then(function (data) {
    console.log(data.toString());
    return readFile(path2);
}).then(function (data) {
    console.log(data.toString());
    return readFile(errorPath);
}).then(function (data) {
    console.log(data.toString());
}).catch(function (e) {
    console.log('error', e);
    return readFile(path1);
}).then(function (data) {
    console.log(data.toString());
});
复制代码

Promise的缺陷:

promise.catch

Generator

generator介绍

Generator是ES6提供的方法,是生成器,最大的特点:可以暂停执行和恢复执行(可以交出函数的执行权),返回的是 指针对象 .

  • 需要自执行函数才能持续运行,否则需要手工调用流程
  • 自执行函数借助promise, 获取异常和最终结果

generator 和 promise自执行实现

const run = function (generator) {
  var g = generator()
  var perform = function (result) {
    if (result.done === true) {
      return result.value
    }
    if (isPromise(result.value)) {
      return result.value.then(function (v) {
        return perform(g.next(v))
      }).catch(function (e) {
        return perform(g.throw(e))
      })
    } else {
      return perform(g.next(result.value))
    }

  }
  return perform(g.next())
}

const isPromise = f => f.constructor === Promise

function* g() {
  var a = yield sleep(1000, _ => 1)
  var b = yield sleep(1000, _ => 2)
  return a + b
}
function sleep(d, fn) {
  return new Promise((resolve, reject) => {
    setTimeout(_ => resolve(fn()), d)
  })
}
复制代码

由于以上问题,于是一个叫 co 库诞生,支持 thunkPromise . 关于thunk可以查看 阮一峰老师的thunk介绍和应用

Async/Await

ES7提供了 async 函数,使得异步操作变得更加方便。

  • 内置执行器
  • 更好的语义
  • 更多的实用场景,co参数Generator函数中 yield 只能是 promisethunk

实例代码:

async function asyncReadFile() {   
    var p1 = readFile(path.join(__dirname, '../data/file1.txt'));
    var p2 = readFile(path.join(__dirname, '../data/file2.txt'));
    var [f1, f2] = await Promise.all([p1, p2]);
    return `${f1.toString()}\n${f2.toString()}`;
}
(async function () {
    try {
        console.log(await asyncReadFile());
    } catch (e) {
        console.log(e.message)
    }
})();
复制代码

Node8 async/await 支持

Node8.0 发布,全面支持 async/await , 推荐使用 async/await , 低版本node可以使用 babel 来编译处理。 而 为了方便 接口设计时 返回  promise 更方面使用者. 当然依然使用 callback , 通过 promisify 做转换, Node8.0 已经内置 util.promisify 方法。

参考资料

小结

异步编程 在javascript中扮演者重要的角色,虽然现在需要通过 babel , typescript 等编译或转换代码,跟着 规范标准 走,就没有跑偏。

好久之前在github博客上的文章了。

如需转载,请备注出处。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

软件架构设计

软件架构设计

温昱 / 电子工业出版社 / 2012-7 / 39.00元

《软件架构设计:程序员向架构师转型必备(第2版)》围绕“软件架构设计”主题,从“程序员”成长的视角,深入浅出地讲述了架构师的修炼之道。从“基础篇”、到“设计过程篇”、到“模块划分专题”,《软件架构设计:程序员向架构师转型必备(第2版)》覆盖了架构设计的关键技能项,并且对于架构设计过程中可能出现的各种问题给与了解答。一起来看看 《软件架构设计》 这本书的介绍吧!

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具