资讯专栏INFORMATION COLUMN

js异同步

leanxi / 2749人阅读

摘要:完成请问应该如何安排操作流程上面代码采用个回调函数的嵌套,不仅写起来麻烦,容易出错,而且难以维护串行执行我们可以编写一个流程控制函数,让它来控制异步任务,一个任务完成以后,再执行另一个。

前言

回调地狱

js异步

Javascript 语言的执行环境是“单线程”(single thread)。所谓“单线程”,就是指一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务。

这种模式的好处是实现起来比较简单,执行环境相对单纯;坏处是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。常见的浏览器无响应(假死),往往就是因为某一段 JavaScript 代码长时间运行(比如死循环),导致整个页面卡在这个地方,其他任务无法执行。

为了解决这个问题,Javascript 语言将任务的执行模式分成两种:同步(Synchronous)和异步(Asynchronous)。

回调函数

假定有两个函数f1和f2,后者必须等到前者执行完成,才能执行。这时,可以考虑改写f1,把f2写成f1的回调函数。

  function f1(callback) {
      callback();
  }
事件监听
f1.on("done", f2);

function f1(){
  setTimeout(function () {
    // f1的任务代码
    f1.trigger("done");
  }, 1000);
}
发布订阅
jQuery.subscribe("done", f2);

function f1(){
    setTimeout(function () {
        // f1的任务代码
        jQuery.publish("done");
    }, 1000);
}

jQuery.unsubscribe("done", f2);

这种方法的性质与”事件监听”类似,但是明显优于后者。因为我们可以通过查看”消息中心”,了解存在多少信号、每个信号有多少订阅者,从而监控程序的运行。

js异步操作流程控制

如果有多个异步操作,就存在一个流程控制的问题:确定操作执行的顺序,以后如何保证遵守这种顺序

function async(arg, callback) {
  console.log("参数为 " + arg +" , 1秒后返回结果");
  setTimeout(function() { callback(arg * 2); }, 1000);
}

上面代码的async函数是一个异步任务,非常耗时,每次执行需要1秒才能完成,然后再调用回调函数。

如果有6个这样的异步任务,需要全部完成后,才能执行下一步的final函数。

function final(value) {
  console.log("完成: ", value);
}

请问应该如何安排操作流程?

async(1, function(value){
  async(value, function(value){
    async(value, function(value){
      async(value, function(value){
        async(value, function(value){
          async(value, final);
        });
      });
    });
  });
});

上面代码采用6个回调函数的嵌套,不仅写起来麻烦,容易出错,而且难以维护

串行执行

我们可以编写一个流程控制函数,让它来控制异步任务,一个任务完成以后,再执行另一个。这就叫串行执行。(任务队列)

 let taskQueen = [1, 2, 3, 4, 5, 6];
 let result = [];
 function invoke(curTask) {
    if (curTask) {
      console.log("当前正在执行任务", curTask);
      result.push(curTask + "完成");
    } else {
      console.log("当前任务全部完成");
    }
 }
 invoke(taskQueen.shift());
var items = [ 1, 2, 3, 4, 5, 6 ];
var results = [];
function series(item) {
  if(item) {
    async( item, function(result) {
      results.push(result);
      return series(items.shift());
    });
  } else {
    return final(results);
  }
}
series(items.shift());
并行
    var items = [ 1, 2, 3, 4, 5, 6 ];
    var results = [];
    items.forEach(function(item) {
      async(item, function(result){
        results.push(result);
        if(results.length == items.length) {
          final(results);
        }
      })
    });

上面代码中,forEach方法会同时发起6个异步任务,等到它们全部完成以后,才会执行final函数。

并行执行的好处是效率较高,比起串行执行一次只能执行一个任务,较为节约时间。但是问题在于如果并行的任务较多,很容易耗尽系统资源,拖慢运行速度。因此有了第三种流程控制方式

function launcher() {
  while(running < limit && items.length > 0) {
    var item = items.shift();
    async(item, function(result) {
      results.push(result);
      running--;
      if(items.length > 0) {
        launcher();
      } else if(running == 0) {
        final(results);
      }
    });
    running++;
  }
}

launcher();
promise

Promise 对象用于一个异步操作的最终完成(或失败)及其结果值的表示。(简单点说就是处理异步请求。我们经常会做些承诺,如果我赢了你就嫁给我,如果输了我就嫁给你之类的诺言。这就是promise的中文含义:诺言,一个成功,一个失败。) -MDN

new Promise(
    /* executor */
    function(resolve, reject) {...}
);

一个 Promise有以下几种状态:

pending: 初始状态,不是成功或失败状态。

fulfilled: 意味着操作成功完成。

rejected: 意味着操作失败。

then()
  var promise = new Promise(function(resolve, reject){
      resolve("传递给then的值");
  });
  promise.then(function (value) {
      console.log(value);
  }, function (error) {
      console.error(error);
  });
catch()

捕获promise 运行的各种错误 promise.then(undefined, onRejected)
的语法糖

var promise = new Promise(function(resolve, reject){
    resolve("传递给then的值");
});
promise.then(function (value) {
    console.log(value);
}).catch(function (error) {
    console.error(error);
});
Promise.resolve && Promise.reject Promise.all

生成并返回一个新的promise对象。

参数传递promise数组中所有的promise对象都变为resolve的时候,该方法才会返回, 新创建的promise则会使用这些promise的值。

如果参数中的任何一个promise为reject的话,则整个Promise.all调用会立即终止,并返回一个reject的新的promise对象。

由于参数数组中的每个元素都是由 Promise.resolve 包装(wrap)的,所以Paomise.all可以处理不同类型的promose对象。

var p1 = Promise.resolve(1),
    p2 = Promise.resolve(2),
    p3 = Promise.resolve(3);
Promise.all([p1, p2, p3]).then(function (results) {
    console.log(results);  // [1, 2, 3]
});
Promise.race
var p1 = Promise.resolve(1),
    p2 = Promise.resolve(2),
    p3 = Promise.resolve(3);
Promise.race([p1, p2, p3]).then(function (value) {
    console.log(value);  // 1
});

生成并返回一个新的promise对象。

参数 promise 数组中的任何一个promise对象如果变为resolve或者reject的话, 该函数就会返回,并使用这个promise对象的值进行resolve或者reject。

参考

JavaScript Promise迷你书(中文版)

promise阮一峰(http://javascript.ruanyifeng....

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

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

相关文章

  • 数据脱敏大数据架构设计

    摘要:需求背景系统有数据识别数据脱敏逻辑,支持可配置规则,自定义等,需要进行异构数据同步,大数据量。可用性分析可用性表格分析场景影响降级原因某台数据同步下线无影响数据同步无状态,调度平台重连其他的数据同步服务。 需求背景 系统有数据识别、数据脱敏逻辑,支持可配置规则,自定义等,需要进行异构数据同步,大数据量。现在针对以下几个需求进行讲解 1、支持冗余设计2、支持任务自动分发,支持自动负载均衡...

    lavor 评论0 收藏0
  • SpringCloud(第 026 篇)简单构系统之 nodejs 微服务

    摘要:第篇简单异构系统之微服务一大致介绍因为在后面要利用集成异构系统,所以才有了本章节的微服务本章节使用了最简单的请求截取的方式,截取不同的后缀做不同的响应处理,简直二实现步骤添加服务端文件引入模块创建获得请求的路径访问,将会返回欢迎 SpringCloud(第 026 篇)简单异构系统之 nodejs 微服务 - 一、大致介绍 1、因为在后面要利用 SpringCloud 集成异构系统,所...

    raledong 评论0 收藏0
  • JS基础——同步步的区别

    摘要:只要指定过这些事件的回调函数,这些事件发生时就会进入任务队列,等待主线程读取。异步任务必须指定回调函数,当主线程开始执行异步任务,就是执行对应的回调函数。 javascript语言是一门单线程的语言,不像java语言,类继承Thread再来个thread.start就可以开辟一个线程。所以,javascript就像一条流水线,仅仅是一条流水线而已,要么加工,要么包装,不能同时进行多个任...

    ztyzz 评论0 收藏0
  • Java String类笔记

    摘要:这两个操作符都是编译器默认引入了类,最后都调用方法返回对象,临时对象被回收,因此效率极为低下 Java String类笔记 声明 文章均为本人技术笔记,转载请注明出处https://segmentfault.com/u/yzwall String的不可变性 String的不可变性 // String declaration public final class String ...

    Vicky 评论0 收藏0
  • 阻塞,非阻塞,步,同步

    摘要:出场人物老张,水壶两把普通水壶,简称水壶会响的水壶,简称响水壶。同步非阻塞老张还是觉得自己有点傻,于是变高端了,买了把会响笛的那种水壶。所谓阻塞非阻塞,仅仅对于老张而言。 1、例子 老张爱喝茶,废话不说,煮开水。出场人物:老张,水壶两把(普通 水壶,简称水壶;会响的水壶,简称响水壶)。 1 老张把水壶放到火上,立等水开。(同步阻塞) 老张觉得自己有点傻 2 老张把...

    AZmake 评论0 收藏0

发表评论

0条评论

leanxi

|高级讲师

TA的文章

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