为什么要异步编程

我们在写前端代码时,经常会对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. windows下CMD常用命令(url链接)

    https://blog.csdn.net/LJFPHP/article/details/78818696

  2. 01--c实现基础客户端和服务端与c++ boost.asio实现对比

    c实现服务端和客户端交互: 学习查阅的博客: https://blog.csdn.net/u011068702/article/details/54380259 https://blog.csdn.n ...

  3. iptables使用详解(centos7)

    安装前 里面有iptables的命令 [root@mcw01 ~]$ rpm -qa|grep iptables iptables-1.4.21-18.0.1.el7.centos.x86_64 [r ...

  4. 【一】TSP、VRP、VRP模型介绍

    一. TSP问题数学模型 编辑 TSP,即Traveling Salesman Problem,也就是旅行商问题,又译为旅行推销员问题.货郎担问题,简称为TSP问题,是最基本的路线问题,该问题是在寻求 ...

  5. 【SQL登录问题】

    essay from:http://www.jb51.net/article/59352.htm 在与 SQL Server 建立连接时出现与网络相关的或特定于实例的错误.未找到或无法访问服务器 今早 ...

  6. 自学java一路以来,心血心得整理分享

    ✿ 贴心提示:文章内容比较长,但都是干货,请大家耐心看完.时间不够充裕的小伙伴,建议收藏,一定要耐心看完,保证对你有后续学习java有所帮助. 一.推荐背景介绍 (一)我是怎么学的? ★ 非常普通的人 ...

  7. LGP3092题解

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

  8. Net中事件的用法之一

    1.事件与委托的关系 委托是一种类型 事件是委托的一个实例 事件中涉及两种角色--事件发布者和事件订阅者. 事件发布者: 触发事件的对象称为事件发布者. 事件订阅者: 捕获事件并对其做出处理的对象称为 ...

  9. Net中委托之一

    1.委托的用法 委托是一种特殊的类型 a. 委托可以类外定义,也可以在类里面定义 b. 委托的操作步骤 1.委托的声明 2.委托的实例化 3.委托的调用 2.委托实例 amespace MyDeleg ...

  10. web note

    web note html note 列表 ul 无序列表 ol 有序列表 并且可以通过 type 来定义列表序号的形式 <!DOCTYPE html> <html> < ...