IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> JavaScript:异步简介与Promise实践拓展 -> 正文阅读

[JavaScript知识库]JavaScript:异步简介与Promise实践拓展

JavaScript语言的一大特点就是单线程,但是在遇到鼠标点击、网络请求、动画执行或者对于Node.js来讲要遇到的数据库操作这一类明显耗时程序的时候,采用单线程的话,会产生明显的阻塞。而对此的解决方案,js是通过任务队列来达到异步的效果。

举个最简单的异步例子

setTimeout(()=>{
	console.log("123");
},1000)
console.log("456")

无疑,这串代码的输出显然是

456
123

异步的常见解决方案

上边这个例子中,我们要使得输出正常地表现为123456,该如何解决?

就算你是新手,也不要尝试用一个1100ms的setTimeout,这很离谱

回调函数——最基本的实现

我曾听过这么一句话:“函数是js中的一等公民”,这句话毋庸置疑,将函数作为值方便地赋给一个变量或是作为参数传递给另一个函数或是作为另一个函数的返回值,这种超高自由度并且极为简易的操作逻辑对于程序员来说是一种迷人的魅力

回调函数就需要用到上边所提到的“将函数作为值赋给一个变量”以及“将函数作为值传递给一个函数”。

function oneFunc(func){
	setTimeout(()=>{
		console.log("123");
		func();
	},1000)
}
oneFunc(()=>{
	console.log("456");
})

上边这份代码通过将console.log(“456”)作为一个函数传入我们要执行的异步代码后,从而实现了异步操作。
回调函数是异步编程最基本的方法,最大的特点是使用简单并且容易理解,缺点是不利于代码的阅读与维护(参考回调地狱);

Generator 函数

function* oneFunc(normalData){
  let data = yield normalData + 1;
  let data1 =yield data*2;
  return data+data1;
}

Generator 函数的特点是它由function* 标识,并且函数体内可能有多个yield,这种函数的调用方式也非常有意思,调用这个函数会返回一个Iterator实例对象。
接着才是真正的调用:

var res=oneFunc(0);
console.log(res.next().value);
//1
console.log(res.next().value);
//2
console.log(res.next().value);
//undenfine

这种模式通过next 方法分阶段执行 Generator 函数,从而完成异步。我对于该方法了解仅限于此,不再多说。

Promise——非常好用的异步神器

Promise的基本用法

Promise对象或者说Promise模式有效地解决了回调函数使得代码阅读性降低的问题。
我们尝试用Promise解决上方的异步问题

var pro=new Promise((resolve,reject)=>{
	setTimeout(()=>{
		console.log("123");
		resolve();
	},1000)
})
pro
.then(()=>{
	console.log("456")
});

Promise其实使用起来并不麻烦,我是将其看作这样三步

  • 初始化Promise对象,传入一个函数,这个函数传入后会立即执行
  • 通过resolve()函数在你认为异步执行完毕的地方将Promise状态置为成功,反之则用reject()置失败
  • 通过Promise对象下的then函数或catch()执行下一步操作

Promise用较大的实践用处,我会在后边说到

async/await语法糖

async实际上是ES7中的内容,它们是建立在Promise基础之上的新写法。

async的用法

这次我实在不想用打印123456的例子了,简述一下async与await的功能,在后边会和Promise实践详细讲讲。
async: 自动封装函数返回结果使得函数返回一个Promise对象
await:等待一个Promise对象进入完成态并且自动获取它的结果,只能在async中使用,如果Promise被reject,会报错


Promise实践

在我的项目实战中,我喜欢通过Promise和async/await来实现异步操作。为此,我希望在讲述我的实战需求的同时,记录下其中的操作

在外部修改Promise的状态

这种需求的来源是我在写小程序时,由于需要本地鉴权,而每次鉴权自然必须通过云端获取用户数据,这显然是一个异步的过程。所以我希望,我在进入某个页面时,默认为无权限状态,之后待权限加载完成根据权限处理相应事件。这种场景常见于刚打开小程序,而鉴权模块显然必须是公共模块加载,也就是必须挂载在app.js下,否则以主页作为加载体会使得分享链接打开无法登录鉴权。
为此,我简易封装了Promise

class XPromise extends Promise{
  constructor(e){
    let rs,rj;
    super((resolve,reject)=>{
      e(resolve,reject);
      rs=resolve;
      rj=reject;
    });
    this.resolve=rs;
    this.reject=rj;
  }
  setSuccess(){
    this.resolve()
  }
  setFail(){
  	try {
      this.reject()
    } catch (error) {
      console.log(error)
    }
  }
}
export {XPromise}

在XPromise中,可以通过setSuccess在对象外部手动将状态修改为成功。而实现原理也非常简单,因为js基于地址寻址,并且函数可以作为变量存在,只要我保存下原本resolve()与reject()函数所在的地址,就可以在外部修改Promise的状态。

让函数返回一个Promise

Promise是强大的,通过Promise可以简单地让异步变得如同同步

async getOpenid() {
    let that = this;
    return new Promise((resolve, reject) => {
      wx.getStorage({
        key: '_openid',
        success: res => {
          console.log('Successful obtained _openid form Storage;')
          that._openid = res.data;
          resolve(res.data);
        },
        fail: e => {
          wx.cloud.callFunction({
            name: 'getOpenid',
            success: res => {
              that._openid = res.result._openid
              wx.setStorage({
                key: '_openid',
                data: res.result._openid
              })
              resolve(res.result._openid);
            },
            fail:e=>{
              reject();
            }
          })
        }
      })
    })
  }

 getOpenid()
 .then(res=>{
	getUserInfo()
	})
 .catch(e=>{})

上例来自于小程序,我将获取openid的模块封装成一个函数,因为很多事务都需要在_openid初始化完之后才能正常展开

修改Promise的完成状态返回值

众所周知,js对于变量采用了引用的机制,只要我们能将Promise返回的值保存进一个固定的变量中,就可以通过修改该变量使得之后在其他地方调用then方法得到一个已经被修改的返回值
下边是一个收藏系统的本地部分的部分代码

class Enshrine {
  constructor() {
    this.enshrinePromise = this.initEnshrineArea()
    setInterval(()=>{
      this.saveEnshrineArea()
    },60000)
  }
  async initEnshrineArea() {
    this.enshrineArea = {};
    return new Promise((rs, rj) => {
      wx.getStorage({
        key: 'enshrineArea',
        success: async res => {
          this.enshrineArea = res.data;
          if (this.enshrineArea=={}) {
            this.enshrineArea=await Enshrine.downloadEnshrine()
            rs(this.enshrineArea)
          }else{
            rs(this.enshrineArea)
          }
        },
        fail: async e => {
          this.enshrineArea=await Enshrine.downloadEnshrine()
          rs(this.enshrineArea)
        }
      })
    })
  }
  async saveEnshrineArea() {
	//保存本地数据操作
  }
  async uploadEnshrine() {
    //数据上传云端
  }
  static async downloadEnshrine() {
    //从云端下载数据
  }
  init(name){
    if (!this.enshrineArea[name]) {
      this.enshrineArea[name]=[]
    }
  }
}
export {
  Enshrine
}

为了减少收藏操作开销,我将收藏系统设计为用户收藏操作会影响内存中数据,每隔一段时间或者经历特定操作会保存数据到本地,在每次打开应用将数据上传到云端。
这种情况下,当我修改enshrineArea 的内容,本质上就是修改Promise对象的返回值

通过async/await简洁地进行异步操作

Node.js基于JavaScript,早期的NodeJS同样是单线程的。
我在使用Express框架以及在最近项目的小程序云开发中,会有必须等待网络请求、数据库操作结果这样的需求。老久之前在Express中我使用了大量的Promise进行函数封装,最后链式调用。而在最近的云开发云函数上,我发现async/await实实在在滴让异步操作同步化。

  • Promise化
return new Promise((resolve,reject)=>{
    db.collection('UserInfo').add({
      data:event
    }).then(res=>{
      resolve(res)
    }).catch(e=>{
      reject(e)
    })
  })
  • async/await
try{
	var data=await func();
	return data;
}catch(e){
	console.log(e)
}
//在func中执行异步操作

可能这个例子并不明显,但是在遇见有多个异步请求。并且他们有着较为复杂关系的时候,我还是更喜欢用async/await的语法糖(当然,这两个语法糖有不少缺点…介意慎用)


Tip 下期可能有啥呢

近期我在设计一个基于MongoDB的论坛数据库,包含文章、一级评论、点赞,我目前虽然已经设计完毕,但一直在思考如何设计可以提高性能的同时不产生太大开销?并且在尝试着非范式设计。

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-07-25 11:33:33  更:2021-07-25 11:33:39 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年4日历 -2024/4/25 9:29:44-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码