js单线程和js异步操作的几种方法

一、为什么JavaScript是单线程?
JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。
JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。
这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。
一种是同步任务(synchronous),另一种是异步任务(asynchronous)。
同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
异步任务指的是,不进入主线程、而进入”任务队列”(task queue)的任务,只有”任务队列”通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
-------------------------------------------------------------------------
js的异步操作的几种方法:1.回调函数
function fn1 () {
  console.log(‘方法一‘)
}

function fn2 () {
  setTimeout(() => {
    console.log(‘方法二‘)
  }, 1000)
}

function fn3 () {
  console.log(‘方法三‘)
}

现在我希望可以依次执行fn1,fn2,fn3。为了保证fn3在最后执行,我们可以把它作为fn2的回调函数:
function fn2 (f) {
  setTimeout(() => {
    console.log(‘方法二‘)
    f()
  }, 1000)
}

fn2(fn3)
由此可以看出fn2和fn3完全耦合在一起,如果多了就耦合度很高;
-----------------------------------------------------------------------------
2.事件发布/订阅(可以在es5下相当优雅地处理异步操作)
fn1,fn2,fn3都可以视作一个事件的发布者,只要执行它,就会发布一个事件。这个时候,我们可以通过一个事件的订阅者去批量订阅并处理这些事件,包括它们的先后顺序。
class AsyncFunArr {
  constructor (...arr) {
    this.funcArr = [...arr]
  }

  next () {
    const fn = this.funcArr.shift()
    if (typeof fn === ‘function‘) fn()
  }

  run () {
    this.next()
  }
}

const asyncFunArr = new AsyncFunArr(fn1, fn2, fn3)
然后在fn1,fn2,fn3内调用其next()方法;
function fn1 () {
  console.log(‘方法一‘)
  asyncFunArr.next()
}

function fn2 () {
  setTimeout(() => {
    console.log(‘方法二‘)
    asyncFunArr.next()
  }, 500)
}

function fn3 () {
  console.log(‘方法三‘)
  asyncFunArr.next()
}

//依次输出方法一、方法二、方法三
---------------------------------------------------------------------------------
3.Promise
function fn1 () {
  console.log(‘方法一‘)
}

function fn2 () {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log(‘方法二‘)
      resolve()
    }, 500)
  })
}

function fn3 () {
  console.log(‘方法三‘)
}

其中fn2是一个返回Promise的异步函数,现在按顺序执行它们;

fn1()
fn2().then(() => { fn3() })

//依次输出方法一、方法二、方法三
---------------------------------------------------------------------------------
4.generator(这种方式还是不够优雅)
function fn1 () {
  console.log(‘方法一‘)
}

function fn2 () {
  setTimeout(() => {
    console.log(‘方法二‘)
    af.next()
  }, 500)
}

function fn3 () {
  console.log(‘方法三‘)
}

function* asyncFunArr (...fn) {
  fn[0]()
  yield fn[1]()
  fn[2]()
}

const af = asyncFunArr(fn1, fn2, fn3)

af.next()

//依次输出方法一、方法二、方法三

如果有多个异步函数,那么这个generator函数肯定得改写,而且在语义化的程度来说也有一点不太直观。
------------------------------------------------------------------------------------
5.优雅的async/await(最新版本的Node已经可以原生支持async/await写法了,通过各种pollyfill也能在旧的浏览器使用。)
function fn1 () {
  console.log(‘方法一‘)
}

function fn2 () {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log(‘方法二‘)
      resolve()
    }, 500)
  })
}

function fn3 () {
  console.log(‘方法三‘)
}

async function asyncFunArr () {
  fn1()
  await fn2()
  fn3()
}

asyncFunArr()

//依次输出方法一、方法二、方法三

异步的操作都返回Promise,需要顺序执行时只需要await相应的函数即可,这种方式在语义化方面非常友好;

原文地址:https://www.cnblogs.com/lhl66/p/8974718.html

时间: 2024-10-08 12:22:22

js单线程和js异步操作的几种方法的相关文章

从浏览器多进程到JS单线程,JS运行机制最全面的一次梳理(转)

前言 见解有限,如有描述不当之处,请帮忙及时指出,如有错误,会及时修正. ----超长文+多图预警,需要花费不少时间.---- 如果看完本文后,还对进程线程傻傻分不清,不清楚浏览器多进程.浏览器内核多线程.JS单线程.JS运行机制的区别.那么请回复我,一定是我写的还不够清晰,我来改... ----正文开始---- 最近发现有不少介绍JS单线程运行机制的文章,但是发现很多都仅仅是介绍某一部分的知识,而且各个地方的说法还不统一,容易造成困惑. 因此准备梳理这块知识点,结合已有的认知,基于网上的大量参

js中常用追加元素的几种方法:append,appendTo,after,before,insertAfter,insertBefore,appendChild

js中常用追加元素的几种方法,点击下面每个按钮,即可查看效果 我是第一个子元素 我是第二个子元素 append appendTo prepend prependTo after before appendChild insertAfter insertBefore

Jsp页面跳转和js控制页面跳转的几种方法

Jsp 页面跳转的几种方法 1. RequestDispatcher.forward() 在服务器端起作用,当使用forward()时,Servlet engine传递HTTP请求从当前的Servlet或者是JSP到另外的一个Servlet.JSP 或普通HTML文件,也即你的form提交至a.jsp,在a.jsp用到了forward()重定向至b.jsp,此时form提交的所有信息在 b.jsp都可以获得,参数自动传递. 但forward()无法重定向至有frame的jsp文件,可以重定向至有

js 动态加载事件的几种方法总结

本篇文章主要是对js 动态加载事件的几种方法进行了详细的总结介绍,需要的朋友可以过来参考下,希望对大家有所帮助 有些时候需要动态加载javascript事件的一些方法往往我们需要在 JS 中动态添加事件,这就涉及到浏览器兼容性问题了,以下谈及的几种方法,我们也常常混合使用. 方法一.setAttributevar obj = document.getElementById("obj");obj.setAttribute("onclick", "javasc

js去掉字符串前后空格的五种方法(转)

出处:http://www.2cto.com/kf/201204/125943.html 第一种:循环检查替换[javascript]//供使用者调用  function trim(s){  return trimRight(trimLeft(s));  }  //去掉左边的空白  function trimLeft(s){  if(s == null) {  return "";  }  var whitespace = new String(" \t\n\r")

JS中date日期初始化的5种方法

原文:JS中date日期初始化的5种方法 创建一个日期对象: 代码如下: var objDate=new Date([arguments list]); 参数形式有以下5种: 1)new Date("month dd,yyyy hh:mm:ss"); 2)new Date("month dd,yyyy"); 3)new Date(yyyy,mth,dd,hh,mm,ss); 在程序中我使用的第三种初始化方法,总是显示格式化的参数不正确,仔细看了一下一定要是整型的才可

js单线程、js任务队列、异步操作

2017.9.22[面试阶段] 一:js由来 JavaScript 1995年首次设计在浏览器上,领导者为了让他看起来更像java,所以起名JavaScript: js是兼容ECMA标准,也称为ECMAScript:js是一种直译式脚本语言,是一种动态类型.弱类型.基于原型的语言: 他的解释器称为JavaScript引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早用在HTML上,添加动态功能 二:单线程是js的一大特性 不像其他语言如java一样多线程,不用考虑线程同步的问题: js是用户

js数组和字符串去重复几种方法

js数组去重复几种方法 第一种:也是最笨的吧. Array.prototype.unique1 = function () { var r = new Array(); label:for(var i = 0, n = this.length; i < n; i++) { for(var x = 0, y = r.length; x < y; x++) { if(r[x] == this[i]) { continue label; } } r[r.length] = this[i]; } re

js实现元素宽度反弹的两种方法

方法一:此方法,不是我想的,但是超级喜欢,太漂亮了 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>02</title> <style> #outer{ width:1400px; height:400px; background-color: pink; } #inner{ width:20