为什么要异步编程

我们在写前端代码时,经常会对dom做事件处理操作,比如点击、激活焦点、失去焦点等;再比如我们用ajax请求数据,使用回调函数获取返回值。这些都属于异步编程。

也许你已经大概知道JavaScript引擎单线程的概念,那么这种单线程模式和异步编程有什么关系呢?

JavaScript引擎中,只有一个主线程,当执行JavaScript代码块时,不允许其他代码块执行,而事件机制和回调机制的代码块会被添加到任务队列(或者叫做堆栈)中,当符合某个触发回调或者事件的时候,就会执行该事件或者回调函数。

上面这段话的意思可以这样理解,假设你是一个修仙者,你去闯一个秘境,这个秘境就是主线程,你只能一直深入下去,直到找到宝物和出口,而你还有一个自身的储物空间,这个空间就类似堆栈,你在储物空间放了很多你可能用到的法宝或者丹药,这些东西就是回调函数和事件函数,当你遇到危险或者满足某个条件时,就可以从储物空间拿出你当前需要的东西。

好吧,不扯这么远,下面看正题。

事件模型:
浏览器初次渲染DOM的时候,我们会给一些DOM绑定事件函数,只有当触发了这些DOM事件函数,才会执行他们。

const btn = document.querySelector('.button')
btn.onclick = function(event) {
console.log(event)
}

回调模式:
nodejs中可能非常常见这种回调模式,但是对于前端来说,ajax的回调是最熟悉不过了。ajax回调有多个状态,当响应成功和失败都有不同的回调函数。

$.post('/router', function(data) {
console.log(data)
})

回调也可能带来一个问题,那就是地狱回调,不过幸运的是,我从进入前端界开始,就使用react,跳过了很多坑,特别是地狱回调,一直没有机会在工作中遇见到,真是遗憾。

Promise

事件函数没有问题,我们用的很爽,问题出在回调函数,尤其是指地狱回调,Promise的出现正是为了避免地狱回调带来的困扰。

推荐你看JavaScript MDN Promise教程,然后再结合本文看,你就能学会使用Promise了。

Promise是什么

Promise的中文意思是承诺,也就是说,JavaScript对你许下一个承诺,会在未来某个时刻兑现承诺。

Promise生命周期

react有生命周期,vue也有生命周期,就连Promise也有生命周期,现在生命周期咋这么流行了。

Promise的生命周期:进行中(pending),已经完成(fulfilled),拒绝(rejected)

Promise被称作异步结果的占位符,它不能直接返回异步函数的执行结果,需要使用then(),当获取异常回调的时候,使用catch()。

这次我们使用axios插件的代码做例子。axios是前端比较热门的http请求插件之一。

1、创建axios实例instance。

import axios from 'axios'
export const instance = axios.create()

2、使用axios实例 + Promise获取返回值。

const promise = instance.get('url')

promise.then(result => console.log(result)).catch(err => console.log(err))

使用Promise构建函数创建新的Promise

Promise构造函数只有一个参数,该参数是一个函数,被称作执行器,执行器有2个参数,分别是resolve()和reject(),一个表示成功的回调,一个表示失败的回调。

new Promise(function(resolve, reject) {
setTimeout(() => resolve(5), 0)
}).then(v => console.log(v)) // 5

记住,Promise实例只能通过resolve或者reject函数来返回,并且使用then()或者catch()获取,不能在new Promise里面直接return,这样是获取不到Promise返回值的。

1、我们也可以使用Promise直接resolve(value)。

Promise.resolve(5).then(v => console.log(v)) // 5

2、也可以使用reject(value)

Promise.reject(5).catch(v => console.log(v)) // 5

3、执行器错误通过catch捕捉。

new Promise(function(resolve, reject) {
if(true) {
throw new Error('error!!')
}
}).catch(v => console.log(v.message)) // error!!

全局的Promise拒绝处理

不重要的内容,不用细看。

这里涉及到nodejs环境和浏览器环境的全局,主要说的是如果执行了Promise.reject(),浏览器或者node环境并不会强制报错,只有在你调用catch的时候,才能知道Promise被拒绝了。

这种行为就像是,你写了一个函数,函数内部有true和false两种状态,而我们希望false的时候抛出错误,但是在Promise中,并不能直接抛出错误,无论Promise是成功还是拒绝状态,你获取Promise生命周期的方法只能通过then()和catch()。

nodejs环境:

node环境下有个对象叫做process,即使你没写过后端node,如果写过前端node服务器,也应该知道可以使用process.ENV_NODE获取环境变量。为了监听Promise的reject(拒绝)情况,NodeJS提供了一个process.on(),类似jQuery的on方法,事件绑定函数。

process.on()有2个事件

unhandledRjection:在一个事件循环中,当Promise执行reject(),并且没有提供catch()时被调用。

正常情况下,你可以使用catch捕捉reject。

Promise.reject("It was my wrong!").catch(v => console.log(v))

但是,有时候你不总是记得使用catch。你就需要使用process.on()

let rejected
rejected = Promise.reject("It was my wrong!") process.on("unhandledRjection", function(reason, promise) {
console.log(reason.message) // It was my wrong!
console.log(rejected === promise) // true
})

rejectionHandled:在一个事件循环后,当Promise执行reject,并且没有提供catch()时被调用。

let rejected
rejected = Promise.reject(new Error("It was my wrong!")) process.on("rejectionHandled", function(promise) {
console.log(rejected === promise) // true
})

异同:

事件循环中、事件循环后,你可能很难理解这2个的区别,但是这不重要,重要的是,如果你通过了catch()方法来捕捉reject操作,那么,这2个事件就不会生效。

浏览器环境:

和node环境一样,都提供了unhandledRjection、rejectionHandled事件,不同的是浏览器环境是通过window对象来定义事件函数。

let rejected
rejected = Promise.reject(new Error("It was my wrong!")) window.rejectionHandled = function(event) {
console.log(event) // true
}
rejectionHandled()

将代码在浏览器控制台执行一遍,你就会发现报错了:Uncaught (in promise) Error: It was my wrong!

耶,你成功了!报错内容正是你写的reject()方法里面的错误提示。

Promise链式调用

这个例子中,使用了3个then,第一个then返回 s * s,第二个then捕获到上一个then的返回值,最后一个then直接输出end。这就叫链式调用,很好理解的。我只使用了then(),实际开发中,你还应该加上catch()。

new Promise(function(resolve, reject) {
try {

resolve(5)

} catch (error) {

reject('It was my wrong!!!')

}
}).then(s => s * s).then(s2 => console.log(s2)).then(() => console.log('end'))
// 25 "end"

Promise的其他方法

在Promise的构造函数中,除了reject()和resolve()之外,还有2个方法,Promise.all()、Promise.race()。

Promise.all():

前面我们的例子都是只有一个Promise,现在我们使用all()方法包装多个Promise实例。

语法很简单:参数只有一个,可迭代对象,可以是数组,或者Symbol类型等。

Promise.all(iterable).then().catch()

示例:传入3个Promise实例

Promise.all([
new Promise(function(resolve, reject) {
resolve(1)
}),
new Promise(function(resolve, reject) {
resolve(2)
}),
new Promise(function(resolve, reject) {
resolve(3)
})
]).then(arr => {
console.log(arr) // [1, 2, 3]
})

Promise.race():语法和all()一样,但是返回值有所不同,race根据传入的多个Promise实例,只要有一个实例resolve或者reject,就只返回该结果,其他实例不再执行。

还是使用上面的例子,只是我给每个resolve加了一个定时器,最终结果返回的是3,因为第三个Promise最快执行。

Promise.race([
new Promise(function(resolve, reject) {
setTimeout(() => resolve(1), 1000)
}),
new Promise(function(resolve, reject) {
setTimeout(() => resolve(2), 100)
}),
new Promise(function(resolve, reject) {
setTimeout(() => resolve(3), 10)
})
]).then(value => {
console.log(value) // 3
})

Promise派生

派生的意思是定义一个新的Promise对象,继承Promise方法和属性。

class MyPromise extends Promise {

  //重新封装then()
success(resolve, reject) {
return this.then(resolve, reject)
}
//重新封装catch()
failer(reject) {
return this.catch(reject)
}
}

接着我们来使用一下这个派生类。


new MyPromise(function(resolve, reject) {
resolve(10)
}).success(v => console.log(v)) // 10

如果只是派生出来和then、catch一样的方法,我想,你不会干这么无聊的事情。

Promise和异步的联系

Promise本身不是异步的,只有他的then()或者catch()方法才是异步,也可以说Promise的返回值是异步的。通常Promise被使用在node,或者是前端的ajax请求、前端DOM渲染顺序等地方。

比Promise更牛逼的异步方案

在本章你只需要了解有async这个未来的方案,推荐不会的赶紧去网上找资料学,反正我是已经在实际项目中全面开展async了。

async function a() {
await function() {}}
}

总结

Promise是什么、怎么用、怎么获取返回值?是本章的中心内容,多看几遍,你会发现使用Promise是非常简单的事情。

=> 返回文章目录

本文转载于:猿2048→https://www.mk2048.com/blog/blog.php?id=h1c1kc2jakj

《深入理解ES6》笔记—— Promise与异步编程(11)的更多相关文章

  1. 【读书笔记】【深入理解ES6】#11-Promise与异步编程

    异步编程的背景知识 JavaScript 引擎是基于单线程(Single-threaded)实际循环的概念构建的,同一时刻只允许一个代码块在执行. 所以需要跟踪即将运行的代码,那些代码被放在一个任务队 ...

  2. Promise和异步编程

    前面的话 JS有很多强大的功能,其中一个是它可以轻松地搞定异步编程.作为一门为Web而生的语言,它从一开始就需要能够响应异步的用户交互,如点击和按键操作等.Node.js用回调函数代替了事件,使异步编 ...

  3. Promise对象 异步编程

    Promise 的含义 Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大.所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是 ...

  4. es6 generator函数的异步编程

    es6 generator函数,我们都知道asycn和await是generator函数的语法糖,那么genertaor怎么样才能实现asycn和await的功能呢? 1.thunk函数    将函数 ...

  5. 深入理解es6的promise

    一.promise入门 1. Promise对象是什么 回调函数的另一种原生实现,比之前回调函数的写法机构清晰,功能强大, 2.以前回调这么写 function a(fn){ let h = 1; s ...

  6. Node.js用ES6原生Promise对异步函数进行封装

    Promise的概念 Promise 对象用于异步(asynchronous)计算..一个Promise对象代表着一个还未完成,但预期将来会完成的操作. Promise的几种状态: pending:初 ...

  7. Promise对异步编程的贡献以及基本API了解

    异步: 核心: 现在运行的部分和将来运行的部分之间的关系 常用方案: 从现在到将来的等待,通常使用一个回调函数在结果返回时得到结果 控制台(因为console族是由宿主环境即游览器实现的)可能会使用异 ...

  8. 深入理解nodejs中的异步编程

    目录 简介 同步异步和阻塞非阻塞 javascript中的回调 回调函数的错误处理 回调地狱 ES6中的Promise 什么是Promise Promise的特点 Promise的优点 Promise ...

  9. 《深入理解ES6》读书笔记

    文章目录 第一章 块级绑定 1. var 声明与变量提升 2. let 与 var 的区别 第二章 字符串与正则表达式 1.字符串扩展 1.1 includes().startsWith() .end ...

随机推荐

  1. MyBatis 使用(XML版本)

    一.MyBatis相关概念 对象 / 关系数据库映射(ORM) ORM全称Object/Relation Mapping:表示对象-关系映射的缩写 ORM完成⾯向对象的编程语⾔到关系数据库的映射.当O ...

  2. LeetCode-001-两数之和

    两数之和 题目描述:给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标. 你可以假设每种输入只会对应一个答 ...

  3. Mock平台2-Java Spring Boot框架基础知识

    微信搜索[大奇测试开],关注这个坚持分享测试开发干货的家伙. 认识Spring Boot 在N年前的大学时代想要开发一个Web服务使用的还是SSH框架(struts+spring+hibernate) ...

  4. 面试官:Java中对象都存放在堆中吗?你知道逃逸分析?

    面试官:Java虚拟机的内存分为哪几个区域? 我(微笑着):程序计数器.虚拟机栈.本地方法栈.堆.方法区 面试官:对象一般存放在哪个区域? 我:堆. 面试官:对象都存放在堆中吗? 我:是的. 面试官: ...

  5. 快速整明白Redis中的字典到底是个啥

    字典简介 字典是一种用于保存键值对的数据结构,可以通过键值对中的键快速地查找到对应的值.在Redis所使用的C语言中,并没有内置字典,所以Redis自己实现了字典. 整个Redis数据库的所有的键和值 ...

  6. Java:基于AOP的动态数据源切换(附源码)

    1 动态数据源的必要性 我们知道,物理服务机的CPU.内存.存储空间.连接数等资源都是有限的,某个时段大量连接同时执行操作,会导致数据库在处理上遇到性能瓶颈.而在复杂的互联网业务场景下,系统流量日益膨 ...

  7. UOJ188题解

    我们先枚举一个最大质因子,然后设 \(dp[n][k]\) 为 \(n\) 以内使用了 \(pri[k]\) 以内的质数的数的最大质因子之和,答案就是: \[\sum_{k\leq n}dp[\lfl ...

  8. LGP3092题解

    看 DP 的时候翻到的题,发现这题的坑鸽子了一年半 这个状态感觉比较厉害,还是来记录一下吧. 首先硬币数量很少让我们想到状压,可以想出来一个十分 navie 的状态:\(dp[S][n]\) 表示用过 ...

  9. Docker容器和虚拟机区别

    Docker .虚拟机之间区别 虚拟机技术的缺点: 1.资源占用太多 2.冗余步骤多 3.启动很慢 容器化技术 1.服务器资源利用率高 2.比较轻量化 3.打包镜像测试,一键运行 比较Docker和虚 ...

  10. ClickHouse镜像在阿里云镜像站首发上线

    镜像下载.域名解析.时间同步请点击阿里云开源镜像站 简介 ClickHouse是开源.高性能的列式OLAP的数据库管理系统(DBMS).使用SQL进行实时分析. ClickHouse可以做用户行为分析 ...