资讯专栏INFORMATION COLUMN

探索Javascript 异步编程

Salamander / 3357人阅读

摘要:因为浏览器环境里是单线程的,所以异步编程在前端领域尤为重要。除此之外,它还有两个特性,使它可以作为异步编程的完整解决方案函数体内外的数据交换和错误处理机制。

在我们日常编码中,需要异步的场景很多,比如读取文件内容、获取远程数据、发送数据到服务端等。因为浏览器环境里Javascript是单线程的,所以异步编程在前端领域尤为重要。

异步的概念

所谓异步,是指当一个过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的过程在完成后,通过状态、通知或者回调来通知调用者。

比如我们写这篇文字时点击发布按钮,我们并不能马上得到文章发布成功或者失败。等待服务器处理,这段时间我们可以做其他的事情,当服务器处理完成后,通知我们是否发布成功。

所谓同步,是指当一个过程调用发出后,必须等待这个过程处理完成后,再处理其他事情。即堵塞执行。

异步的方式

es6之前,我们实现异步有4种方法,回调、事件、发布订阅和promise方式。

异步之回调:
function dealTask(param, callback) {
    // Deal with some time consuming tasks.
    // ...
    Object.prototype.toString.call(callback) === "[object Function]" ? callback() : null; 
}
 
dealTask({ id: 1 }, function() {
    console.log("... I am in the callback...");
})

回调的方式来实现异步其实就是把需要在当前任务完成后执行的函数当成参数传入,完成任务后执行即可。

异步之事件
function dealTask(param) {
    // Deal with some time consuming tasks.
    // ...
    events.trigger("dealTaskFinish")
}
events.on("dealTaskFinish", function() {
    console.log("...I am in the end...");
})

通过事件来实现回调,好处是方便实用,跨模块传递数据。坏处是,事件用的多了后业务逻辑混乱,不知道哪里注册过哪里监听过。

另外需要注意的是在web component场景下,mount后注册过的事件需要在unmount释放,不然会导致内存泄露。

异步之发布订阅

发布订阅的简单例子是,一个开关,同时并联几个灯泡(在不同房间),触发的时候,几个灯泡都会得到指令,然后执行发光的行为。

// 使用pubsubz实现
var testSubscriber = function(data ){
    console.log(data );
};
var testSubscription = pubsubz.subscribe( "example", testSubscriber );
pubsubz.publish( "example", "hello" );

订阅发布与的性质与"事件监听"类似,不同的是,我们可以通过查看"消息中心",了解存在多少信号、每个信号有多少订阅者,从而监控程序的运行。

异步之Promise
function helloWorld (ready) {
    return new Promise(function (resolve, reject) {
        if (ready) {
            resolve("Hello World!")
        } else {
            reject("Good bye!")
        }
    })
}

helloWorld(true).then(function (message) {
    console.log(message)
}, function (error) {
    console.log(error)
})

Promises对象是CommonJS工作组提出的一种规范,是对异步编程的一种统一,其实也就是语法糖,可阅读性变强了而已。

在ES6出来以后,我们的异步方式也发生了改变。

异步之Generator

Generator函数是协程在ES6的实现,最大特点就是可以交出函数的执行权(即暂停执行)。

Generator函数可以暂停执行和恢复执行,这是它能封装异步任务的根本原因。除此之外,它还有两个特性,使它可以作为异步编程的完整解决方案:函数体内外的数据交换和错误处理机制。

function* Foo(x) {
  yield x + 1;

  var y = yield null;
  return x + y;
}

var foo = Foo(5);
foo.next();     // { value: 6, done: false }
foo.next();     // { value: null, done: false }
foo.next(8);    // { value: 13, done: true }

next方法返回值的value属性,是Generator函数向外输出数据;next方法还可以接受参数,这是向Generator函数体内输入数据。

yield命令用于将程序的执行权移出Generator函数,那么就需要一种方法,将执行权再交还给Generator函数

上面的方式,是我们手动调用Generator函数执行,但是当我们的需要执行next方法很多时,就需要Generator函数自动执行了。

Generator函数自动执行的意思是,通过一定的方法来自动执行next方法,比如:

function autoRunGen(gen){
  var g = gen();

  function next(data){
    var result = g.next(data);
    if (result.done) return result.value;
    result.value.then(function(data){
      next(data);
    });
  }
  next();
}

co模块是TJ开发的一个小工具,用于Generator函数的自动执行。他的主要思想和上面的代码片段类似。

使用co的前提条件是,Generator函数的yield命令后面,只能是Promise对象。

co(function *() {
    var data = yield $.get("/api/data");
    console.log(data);
    var user = yield $.get("/api/user");
    console.log(user);
    var products = yield $.get("/api/products");
    console.log(products);
});

co模块使得我们可以像写同步代码一样,写异步代码。

异步之async/await

async函数仅仅是Generator函数的语法糖。

var fs = require("fs");

var readFile = function (fileName) {
  return new Promise(function (resolve, reject) {
    fs.readFile(fileName, function(error, data) {
      if (error) reject(error);
      resolve(data);
    });
  });
};

var gen = function* (){
  var f1 = yield readFile("/etc/a.js");
  var f2 = yield readFile("/etc/b.js");
  console.log(f1.toString());
  console.log(f2.toString());
};

使用async/await方式:

var asyncReadFile = async function (){
  var f1 = await readFile("/etc/a.js");
  var f2 = await readFile("/etc/b.js");
  console.log(f1.toString());
  console.log(f2.toString());
};

async函数就是将Generator函数的星号(*)替换成async,将yield替换成await,仅此而已。

不同的是,Generator执行需要手动执行,而async函数可以自动执行,像写同步一样写异步。Generator返回Iterator对象,async函数返回Promise对象。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/90894.html

相关文章

  • JavaScript模块化编程探索

    摘要:模块化编程,已经成为一个迫切的需求。随着网站功能逐渐丰富,网页中的也变得越来越复杂和臃肿,原有通过标签来导入一个个的文件这种方式已经不能满足现在互联网开发模式,我们需要团队协作模块复用单元测试等等一系列复杂的需求。 随着网站逐渐变成互联网应用程序,嵌入网页的Javascript代码越来越庞大,越来越复杂。网页越来越像桌面程序,需要一个团队分工协作、进度管理、单元测试等等......开发...

    jayzou 评论0 收藏0
  • 翻译连载 | 第 10 章:异步的函数式(上)-《JavaScript轻量级函数式编程》 |《你不知

    摘要:这就是积极的函数式编程。上一章翻译连载第章递归下轻量级函数式编程你不知道的姊妹篇原创新书移动前端高效开发实战已在亚马逊京东当当开售。 原文地址:Functional-Light-JS 原文作者:Kyle Simpson-《You-Dont-Know-JS》作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTML 最坚实的梁柱;分享,是 CSS 里最闪耀的一瞥;总...

    Lucky_Boy 评论0 收藏0
  • JavaScript是如何工作的:Web Workers的构建块+ 5个使用他们的场景

    摘要:最后,提供个正确使用的场景。异步编程的一个很好的用例就请求。这意味着异步函数只能解决一小部分语言单线程中的局限性问题。中有类似的集群子进程概念,他们也是多线程但是和还是有区别。可用的特性由于的多线程特性,工作者只能访问特性的一个子集。 showImg(https://segmentfault.com/img/bVblS8J?w=400&h=298); 这是专门探索 JavaScript...

    ningwang 评论0 收藏0
  • Promise in ECMAScript | 前端技术探索

    摘要:而纳入规范的也是建立在基础上的。继续阅读的相关解释语法其中函数拥有两个参数和。可以看到,在语法上看,还是有点像回调函数那种形式的,囧。完成操作已经成功执行完毕。消费,即对的所代表的值进行一系列的处理。 文 | Leigh,UPYUN 已获得授权原文链接:http://t.cn/R403hc4 在 JavaScript 这么多年发展中,尤其在前端领域框架层出不穷,解决方案也琳琅满目,Pr...

    SillyMonkey 评论0 收藏0
  • Build Your Own Promise

    摘要:意味着代指的操作由于某些原因失败。第一步构造函数有三种状态,。这个构造函数我们可以先这样写创建一个时,首先进行状态初始化。所有的都是的,而并不是所有的对象都是。 一、JavaScript异步编程背景 ​ 从去年ES2015发布至今,已经过去了一年多,ES2015发布的新的语言特性中最为流行的也就莫过于Promise了,Promise使得如今JavaScript异步编程如此轻松惬意...

    susheng 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<