JavaScript中的异步

这篇具有很好参考价值的文章主要介绍了JavaScript中的异步。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、什么叫异步?

在JS中有同步和异步两种模式

1、同步(Synchronous)
  • 一般指后一个任务等待前一个任务结束,程序的执行顺序与任务的排列顺序是一致的
2、异步(Asynchronous)
  • 每一个任务有一个或多个回调函数,前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务·结束就执行;所以程序的执行顺序与任务的排列顺序是不一致的;
  • 另一种简单的解释:一个任务分两段,先执行第一段,再执行其他任务;当第一段有执行结果时,回去执行第二段

二、JS中为什么要设计异步??

JS是单线程,设计异步可以提高CPU的利用率;但增加了开发难度

1、JS为什么要设计成单线程???

单线程:即同一时间只能做一件事情;JS作为脚本语言,主要的作用是与用户互动、操作DOM,这决定了JS只能是单线程的;否则会带来很复杂的同步问题;

比如:当JS是多线程的,存在有两个线程A、B,线程A想要删除dom1,线程B想要在dom1添加内柔,此时就出现了冲突,不知道应该执行哪个线程的操作。

HTML5提出Web Worker标准允许脚本创建多个线程,但是子线程完全受主线程的控制且不得操作DOM;因为该新标准并没有改变JS单线程的本质

三、事件循环 Event Loop

主线程:JS的首要任务,可以理解为同步任务,先进先出;
执行栈:执行栈是一个先入后出的数据结构,一个同步任务来了进入栈底马上执行它,然后释放;
任务队列:任务队列的存在是为了应付一些异步任务,在执行一个任务如何迟迟等不到不能一直等待,必须让程序接着往下进行;任务队列用于存放这些任务;先进先出;
宏任务:较常见的:script全部代码、setTimeout、setInterval、setImmediate、I/O、UI Rendering
微任务:较常见的:Process.nextTick、Promise(new Promise的过程属于同步任务,resove或者reject之后才算微任务)

Event Loop的流程
  1. 主线程首先按照顺序执行同步任务,将所有同步任务处理完;
  2. 查看微任务队列里是否存在微任务,若存在;将所有微任务队列按顺序执行掉
  3. 查看宏任务队列是否存在宏任务,若存在先把第一个宏任务执行掉
  4. 一直重复步骤2 、3,直到所有微任务和宏任务执行完毕
console.log('script start'); //同步任务

setTimeout(function() {
  console.log('setTimeout');//宏任务
}, 0);

Promise.resolve().then(function() {
  console.log('promise1');//微任务
}).then(function() {
  console.log('promise2');  //微任务
});

console.log('script end');//同步任务
//根据事件循环规则,结果是:script start、script end、promise1、promise2、setTimeout

四、实现异步的方式

ES6诞生以前,异步编程的方法大概有:回调函数、事件监听、发布/订阅、Promise对象
ES6诞生之后,Generator函数将异步编程带入了一个新阶段

1、回调函数

异步编程最基本的方法就是回调函数;回调函数是一段可执行的代码段,它作为一个参数传递给其他代码,其作用是在需要的时候方便调用这段代码(回调函数)。“作为参数传递到另一个函数中,这个参数的函数就是回调函数”
特点:

  1. 不会立即执行:回调函数在调用函数数中也要通过()运算符调用才会执行
  2. 是一个闭包
  3. 执行前会判断是否是一个函数
function add(num1, num2, callback) {
            var sum = num1 + num2;
            if (typeof callback === 'function') {
               //callback是一个函数,才能当回调函数使用
               callback(sum);
           }
}
function print(num) {
    console.log(num);
}
add(1, 2, print); //3
关于回调函数this指向问题

回调函数调用时this的执行上下文并不是回调函数定义时的上下文;而是调用它的函数所在的上下文
当回调函数是普通函数时,当做参数传入另外的函数时,若不知道这个函数内部怎么调用回调函数,就会穿线回调函数中this指向不明确的问题;

function createData(callback){
    callback();
}
var obj ={
     data:100,
     tool:function(){
	         createData(function(n){
	             console.log(this);  //window
	             //this指向是离它最近的或者嵌套级别的function的调用者
	         })   
     }
 }
obj.tool();
解决回调函数this指向的方法
  1. 箭头函数
    把箭头函数当做回调函数,作为参数传入另外一个函数中就不会出现上述代码中this指向不明的问题
function createData(callback){
	callback();
}
var obj ={
     data:100,
     tool:function(){
			createData((n)=>{
			     this.data = n;  //this是obj
			 })   
    	 }
}
obj.tool();
  1. 记录this
 function createData(callback){
 	callback(999);
 }
var obj ={
     data:100,
     tool:function(){
         var self = this;   //这里的this指向obj,然后当一个变量取用
         createData(function(n){
             self.data = n;
         })   
     }
 }
 obj.tool();

2、promise对象

Promise对象是CommonJS提出来的一种规范,目的是为了异步编程提供统一接口
中心思想:每一个异步任务返回一个promise对象,该对象有一个then方法,允许指定回调函数
特点:(1)对象状态不受外界影响;(2)一旦状态改变看就不会在变化;任何时候promise都只有一种状态

(1)promise的状态
  • Pending:进行中
  • Resolved:已完成,promise从Pending状态开始,如果成功就转到成功态,并执行resolve回调函数
  • Rejected:已失败,promise从Pending状态开始,如果成功就转到失败态,并执行reject回调函数
(2)基本使用

通过Promise构造函数创建promise对象,Promise构造函数的参数是一个函数,该函数的两个参数是resolve,reject
当Promise对象状态变为Resolved,则调用resolve并将操作结果作为其参数传递出去;
当Promise对象状态变为Rejected,则调用reject并将操作报出的错误作为其参数传递出去;

var promise = new Promise(function(resolve,reject)
	//一些操作
});    
(3)then方法和catch方法
  • then方法有三个参数:成功回调、失败回调、前进回调;较常用的是只传第一个参数:成功回调
    Promise执行then后还是Promise,因此可以链式调用;并且Promise的执行是异步的
  • catch方法有一个参数:失败回调;.catch(onRejected) 相当于 .then(undefined, onRejected);它返回了一个失败的promise以及返回一个参数作为一个失败理由。promise的错误抛出后会一直传递到最外层直到被捕获;当catch捕获错误以后,返回的还是一个promise对象,可以继续链式调用
new Promise(function(resolve,reject){
	console.log('hi~~~~~')  //这是同步代码
    resolve("hello  world");
    reject('error')
	}).then(v=>{
		console.log('成功回调',v);  //打印resolve传递的参数 hello  world 
	}, e=>{
		console.log('失败回调',e);  //打印reject传递的参数 error
	})
	//相当于
	new Promise(function(resolve,reject){
	console.log('hi~~~~~')  //这是同步代码
    resolve("hello  world");
    reject('error')
	}).then(v=>{
		console.log('成功回调',v);  //打印resolve传递的参数 hello  world 
	}).catch(e=>{
		console.log('失败回调',e);  //打印reject传递的参数 error
	})
(4)all方法
  • Promise.all 生成并返回一个新的 Promise 对象;参数传递promise数组中所有的 Promise 对象都变为resolve的时候,该方法才会返回, 新创建的 Promise 则会使用这些 promise 的值。
  • 如果参数中的任何一个promise为reject的话,则整个Promise.all调用会立即终止,并返回一个reject的新的Promise 对象
var p1 = Promise.resolve(1)
var p2 = Promise.resolve({a:2})
var p3 = new Promise(function(resolve,reject){
	setTimeout(function(){
		resolve(3)
	},3000)
})
Promise.all([p1,p2,p3]).then(result=>{
	// 返回的结果是按照Array中编写实例的顺序来
	console.log(result)  // 1 {a:2}  3
})
(5)race方法

“赛跑”,接收一个promise对象数组为参数;只要有一个promise对象进入FulFilled或者Rejected状态的话,就会继续进行后面的处理

// `delay`毫秒后执行resolve
function timerPromisefy(delay) {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(delay);
        }, delay);
    });
}
// 任何一个promise变为resolve或reject的话程序就停止运行
Promise.race([
    timerPromisefy(10),
    timerPromisefy(32),
    timerPromisefy(64)
]).then(function (value) {
    console.log(value);    // => 10
});
//在第一个变为确定状态的10ms后,.then注册的回调函数就会被调用
(6)finally函数

ES9 新增,finally() 方法返回一个Promise。在promise结束时,无论结果是fulfilled或者是rejected,都会执行指定的回调函数。

3、发布/订阅

发布订阅模式是完全解耦的,发布者、消息中心、订阅者三方的业务逻辑完全独立的,之间仅通过消息来传递。在观察者模式中,被观察者(Subject)只需要一套观察者的集合(Observer),将有关状态的任何变更自动通知给观察者(watcher),这个设计是松耦合的。

4、生成器函数 Generator/yield

协程:多个线程互相协作,完成异步任务
协程的运行流程:

1、协程A开始执行;
2、协程A执行到一半,进入暂停状态,执行权转移到协程B;
3、一段时间后,协程B将执行权归还
4、协程A恢复执行

协程的Generator函数实现:
	function *gen(x){
		let y = yield x+2
		return y
	}
	let g = gen(1)
	g.next()  // {value:3, done:false}
	g.next() //{value:undefiend, true}

Generator函数最大的特点就是交出函数的执行权,整个Generator函数就是一个封装的异步任务,或者说是异步任务的容器。调用Generator函数会返回一个内部指针(即遍历器);调用指针的next方法可以移动内部指针,指向第一个遇到yield语句。
换言之.next方法的作用是分阶段执行,每次调用next方法会返回一个对象,表示当前阶段的信息;value:yield语句后面表达式的值,表示当前阶段的值;done属性:一个布尔值,表示Generator函数是否执行完毕,即是否还有下一个阶段

异步任务的封装:
let fetch = require('node-fetch');
function *gen(){
	let url = 'https://api.github.com/users/github';
	let result = yield fetch(url)
}

let g = gen()//首先执行Generator函数获取遍历器对象
let result = g.next() //执行异步任务的第一阶段
//由于fetch返回的是一个promise,因此可以调用.then方法
result.value.then((data)=>{
	return data.json()
	}).then(()=>{
		g.next(data)
		})

5、async和await函数

ES2017标准引入了async函数; 它是Generator的语法糖,async函数就是将Generator函数的(*)替换成了async,将yield替换成了await。
async是异步的意思,而 await 是等待的意思,await 用于等待一个异步任务执行完成的结果;建立在promise的基础上。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果;async函数的返回值是promise对象,可以使用then方法添加回调函数。文章来源地址https://www.toymoban.com/news/detail-740442.html

	function timeout(ms){
		return new Promise(resolve=>{
			setTimeout(resolve, ms)
		})
	}
	async function asyncPrint(value, ms) {
		awiat timeout(ms)  //正常情况下,await命令后面是一个promise;如果不是会被转成一个立即resolve的promise对象
		console.log(value)
	}
	asyncPrint('你好',50)
	//50ms后打印'你好'

到了这里,关于JavaScript中的异步的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 【JavaScript】3.4 JavaScript在现代前端开发中的应用

    【JavaScript】3.4 JavaScript在现代前端开发中的应用

    JavaScript 是现代前端开发的核心。无论是交互效果,还是复杂的前端应用,JavaScript 都发挥着关键作用。在本章节中,我们将探讨 JavaScript 在现代前端开发中的应用,包括如何使用 JavaScript 来处理用户交互、动态内容、前端路由、API 请求等。 JavaScript 是处理用户交互的主要工

    2024年02月04日
    浏览(13)
  • web前端框架JS学习之JavaScript类型转换

    web前端框架JS学习之JavaScript类型转换

    vascript有多种数据类型,如字符串、数字、布尔等,可以通过typeof语句来查看变量的数据类型。数据类型转换就是数据类型之间相互转换,比如把数字转成字符串、把布尔值转成字符串、把字符串转成数字等,这在工作也是经常碰到的。 本期我们就给大家说说web前端框架JS学

    2024年02月10日
    浏览(47)
  • 【前端灵魂脚本语言JavaScript⑤】——JS中数组的使用

    【前端灵魂脚本语言JavaScript⑤】——JS中数组的使用

    🐚 作者: 阿伟 💂 个人主页: Flyme awei 🐋 希望大家多多支持😘一起进步呀! 💬 文章对你有帮助👉关注✨点赞👍收藏📂 第一种: var 数组名 = new Array(); 创建一个空数组 第二种: var arr2 = new Array(10); 创建一个定长为10的数组 第三种 var arr3 = new Array(a,b,c); 创建时直接指定元素值

    2023年04月08日
    浏览(55)
  • 【JS】JavaScript中的this关键字

    目录 this是什么? this的指向 ①全局环境 ②构造函数 ③对象的方法 this的四类调用方式 ①作为对象方法调用 ②纯粹的函数调用 ③作为构造函数调用 ④使用apply、call、bind调用 举例说明 JavaScript  this  指的是它所属的对象。 它拥有不同的值,具体取决于它的使用位置:

    2024年02月14日
    浏览(12)
  • 〖大前端 - 基础入门三大核心之JS篇㉓〗- JavaScript 的「数组」

    〖大前端 - 基础入门三大核心之JS篇㉓〗- JavaScript 的「数组」

    当前子专栏 基础入门三大核心篇 是免费开放阶段 。 推荐他人订阅,可获取扣除平台费用后的35%收益,文末名片加V! 说明:该文属于 大前端全栈架构白宝书专栏, 目前阶段免费开放 , 购买任意白宝书体系化专栏可加入 TFS-CLUB 私域社区。 福利:除了通过订阅\\\"白宝书系列专

    2024年02月04日
    浏览(13)
  • [前端系列第3弹]JS入门教程:从零开始学习JavaScript

    本文将带领大家,从零开始学习JavaScript,fighting~ 目录 一、JavaScript简介 二、变量和数据类型 三、注释和分号 四、算术运算符 五、表达式和语句 六、代码块和作用域 七、函数(最重要)          JavaScript(简称JS)是一种运行在浏览器中的脚本语言,它可以让网页变得

    2024年02月13日
    浏览(47)
  • 【前端|Javascript第5篇】全网最详细的JS的内置对象文章!

    【前端|Javascript第5篇】全网最详细的JS的内置对象文章!

    前言 在当今数字时代,前端技术正日益成为塑造用户体验的关键。我们在开发中需要用到很多js的内置对象的一些属性来帮助我们更快速的进行开发。或许你是刚踏入前端领域的小白,或者是希望深入了解内置对象的开发者,不论你的经验如何,本篇博客都将给你详细的讲解

    2024年02月12日
    浏览(49)
  • 【零基础学JS - 14 】javaScript中的switch语句

    【零基础学JS - 14 】javaScript中的switch语句

    👨‍💻 作者简介:程序员半夏 , 一名全栈程序员,擅长使用各种编程语言和框架,如JavaScript、React、Node.js、Java、Python、Django、MySQL等.专注于大前端与后端的硬核干货分享,同时是一个随缘更新的UP主. 你可以在各个平台找到我! 🏆 本文收录于专栏: 零基础学JavaScript,包含Jav

    2024年02月07日
    浏览(9)
  • 〖大前端 - 基础入门三大核心之JS篇㉟〗- JavaScript 的DOM简介

    〖大前端 - 基础入门三大核心之JS篇㉟〗- JavaScript 的DOM简介

    说明:该文属于 大前端全栈架构白宝书专栏, 目前阶段免费 , 如需要项目实战或者是体系化资源,文末名片加V! 作者:不渴望力量的哈士奇(哈哥),十余年工作经验, 从事过全栈研发、产品经理等工作,目前在公司担任研发部门CTO。 荣誉: 2022年度博客之星Top4、2023年度超

    2024年02月04日
    浏览(15)
  • [javascript核心-09] 彻底解决js中的类型检测方案

    typeof 基于数据类型的值(二进制)进行检测 返回结果为字符串 typeof NaN 结果为 number typeof null 结果为 Object .对象存储以 000 开头,而 null 也是如此。 typeof 不能细分对象,结果都是 Object typeof function(){} 结果为 function instanceof 检测某个构造函数是否出现在某实例的原型链上 返回结

    2024年02月16日
    浏览(15)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包