关于 Promise 的一些简单理解
一、ES6 中的 Promise
1、JS 如何解决 异步问题?
(1)什么是 同步、异步?
同步指的是 需要等待 前一个处理 完成,才会进行 下一个处理。
异步指的是 不需要等待 前一个处理 完成,就可以进行下一个处理。
(2)JS 是单线程 还是 多线程的?
JS 是单线程的,也即执行处理时 采用 同步机制。而 JS 实现异步 是借助 浏览器的 多线程机制 完成的。
JS 作为浏览器的脚本语言,其根本目的是 实现用户 与 浏览器进行交互,假如现在用户 需要删除一个节点 A,但同时又向节点 A 中添加节点 时,若 JS 为多线程,则一个线程用于删除,一个线程用于添加,那此时浏览器应该以哪个线程为准,这就涉及到了复杂的同步问题。而 JS 若为单线程,则按实际顺序执行即可,不必担心线程之间的冲突。
(3)JS 如何解决同步、异步问题?
JS 是单线程的,也即意味着 下一个任务 需要等待 上一个任务完成后才会去处理,如果上一个任务执行时间非常长,那么将会陷入长时间等待,此方式肯定不可取。
那么可以将 需要长时间处理 或者 不需要立即处理 的任务 抽取出来,等待其 处理完成后 再去执行,从而使 JS 可以继续处理下一个任务。也即 异步处理。
JS 是借助 浏览器的多线程机制 去实现异步处理。
【实现异步大致流程:】
Step1:将 JS 执行过程视为 主线程,也即同步执行 JS 中的代码。
Step2:主线程执行过程中,若发现了异步任务,则将其交给浏览器(浏览器创建多个线程),继续进行下一步处理。
且维护一个 异步任务结果队列(回调函数队列),异步任务完成后,向 异步任务结果队列 中 放置一个事件(即 回调函数)。
Step3:主线程执行完 所有的同步代码后,开始监听 回调函数 队列,若发现 某个回调函数 状态已完成,则执行该回调函数。
2、什么是 Promise?
(1)简单理解?
Promise 是异步编程的一种解决方案。可以理解为一个容器,里面保存着未来才会结束的某个操作(异步操作)的结果,通过 Promise 对象可以获取异步操作的结果。
(2)直观理解一下?
直接使用 console.dir(Promise) 控制台打印一下 Promise 对象的属性。
如下图所示,Promise 为一个构造函数,通过 new Promise() 构建出来的对象可有相应的 catch、then 等方法。
(3)Promise 的特点
特点一:对象的状态不受外界影响。
Promise 有三种状态,Pending (进行中)、Resolved (解决)、Rejected (失败)。
只有异步操作的结果能决定 Promise 处于哪种状态,其余操作无法改变该状态,无法中途取消操作。
特点二:状态改变后,不再变化。
状态改变的情况,Pending -> Resolved 、 Pending -> Rejected。
一旦状态改变,其不会再改变。
(4)如何使用?
需要使用 new 去实例化一个 Promise 对象,参数为 一个函数。
函数的参数为 resolve、reject ,分别表示两个回调函数,由 JavaScript 引擎提供。
resolve 回调函数是改变状态, Pending -> Resolved ,即异步操作成功后调用,并将异步操作的成功结果作为参数向外传递。
reject 回调函数也是改变状态, Pending -> Rejected,即异步操作失败后调用,并将异步操作的失败结果作为参数向外传递。
使用 then 方法可以处理 resolve、reject 传递出来的结果。其接受两个回调函数作为参数。第一个回调函数用来处理 resolve 传递的结果,第二个回调函数用来处理 reject 传递的结果。第二个回调函数可选。
一般情况下,可使用 catch 方法处理 reject 传递出来的结果。其作用等价于 then 方法中的第二个回调函数。
注:
new promise() 的过程仍属于同步(同步触发 异步操作),
而进行 then()、catch() 的过程才真正意义上属于 异步(也即回调状态变化后触发)。
【格式一:(直接通过对象操作)】
var promise = new Promise((resolve, reject) => {
if(异步操作成功) {
resolve(data);
} else {
reject(error);
}
}); promise.then((data) => {
// 成功的操作
}).catch((error) => {
// 失败的操作
}); 【格式二:(封装成方法进行操作)】
function promise() {
return new Promise((resolve, reject) => {
if(异步操作成功) {
resolve(data);
} else {
reject(error);
}
});
} promise().then((data) => {
// 成功的操作
}).catch((error) => {
// 失败的操作
});
(5)比较一下 Promise 与 回调函数的区别
new Promise() 返回一个 promise 对象,通过其 then() 方法处理 异步成功的 后续操作,也即相当于异步成功后 进行 回调函数处理。
那直接传 回调函数 并处理不也一样吗?如下代码功能相同。
【Promise 写法:】
function promise() {
return new Promise((resolve, reject) => {
//做一些异步操作
setTimeout(function(){
resolve('随便什么数据');
}, 2000);
});
} promise().then((data) => {
console.log(data);
}); 【callback 写法:(功能等同于上一种写法)】
function promise2(callback) {
//做一些异步操作
setTimeout(callback, 2000);
} promise2(function() {
console.log('回调函数')
});
那为什么还要引入 promise 呢?promise 另一个特点就是可以 链式调用,当出现 多层回调时,可以简化代码的处理(比 callback 写法更简单、灵活)。
callback 多层回调,一层套一层,不易维护。而 promise 可以随时切换 下一个回调的逻辑(通过 then 指定下一个回调处理),从而简化代码的处理。
【Promise 写法:(then 方法可以向后接着传递 promise 对象 或者 直接传递值,实现多层回调)】
function promise() {
return new Promise((resolve, reject) => {
//做一些异步操作
setTimeout(function(){
console.log('第一次回调');
resolve('随便什么数据');
}, 2000);
});
} function promise2() {
return new Promise((resolve, reject) => {
//做一些异步操作
setTimeout(function(){
console.log('第二次回调');
resolve('随便什么数据');
}, 2000);
});
} function promise3() {
return new Promise((resolve, reject) => {
//做一些异步操作
setTimeout(function(){
console.log('第三次回调');
resolve('随便什么数据');
}, 2000);
});
} promise().then((data) => {
console.log(data);
return promise2();
}).then((data) => {
console.log(data);
return promise3();
}).then((data) => {
console.log(data);
return '第四次回调';
}).then((data) => {
console.log(data);
}); 【callback 写法:(功能等同于上一种写法,但是一层套一层,容易出错,维护起来也麻烦)】
function promise4(callback) {
//做一些异步操作
setTimeout(callback, 2000);
} function callback() {
setTimeout(function() {
console.log("第一次回调");
callback2();
}, 2000);
} function callback2() {
setTimeout(function() {
console.log("第二次回调");
callback3();
}, 2000);
} function callback3() {
setTimeout(function() {
console.log("第三次回调");
}, 2000);
} promise4(callback());
(6)promise 关于 all() 的使用
all() 提供了 同步执行异步操作的能力,其接收 promise 数组作为参数,表示当 所有的异步 pormise 均处理完成后 才会 继续执行。
所有异步操作会 同时处理,全部成功后,会将所有异步返回的数据 封装成数组并 传递给 then(),若有一个异步处理失败,则其会进入 catch()。
注:
all() 最终完成时间 以 最慢的异步操作为准,所有异步操作均成功后才会被 then 处理。
all() 提供同步触发 promise 异步操作的过程,若在某个中间 promise 中定义了死循环,那么后面的 js 代码将都会被卡死、不会执行。
使用场景:
适用于需要同时执行多个异步操作的场景。
比如:游戏加载页面,需要异步加载资源文件,等待所有资源加载完毕后,开始执行游戏。
【all() 举例:】
function promise() {
return new Promise((resolve, reject) => {
//做一些异步操作
setTimeout(function(){
console.log('第一次回调');
resolve('第一次回调成功');
}, 2000);
});
} function promise2() {
return new Promise((resolve, reject) => {
//做一些异步操作
setTimeout(function(){
console.log('第二次回调');
resolve('第二次回调成功');
}, 2000);
});
} Promise.all([promise(), promise2()])
.then(function(results) {
console.log("2 个回调全部执行成功");
console.log(results);
}).catch(function(errors) {
console.log("2 个回调中至少一个执行失败");
console.log(errors);
}); 【输出结果:】
第一次回调
第二次回调
2 个回调全部执行成功
["第一次回调成功", "第二次回调成功"]
(7)promise 关于 race() 与 any() 的使用
race() 、any() 也可以同步处理多个 异步操作,但是稍微有些区别。
any() 强调的是 多个异步操作中,第一个成功执行的 异步操作,会进入 then() 中,若全部失败,则进入 catch()。
race() 强调的是 多个异步操作中,第一个执行完成的 异步操作,若第一个执行完成的操作出错,则进入 catch(),否则进入 then()。
使用场景:
race() 可用于给某个 异步请求 设置超时时间。
比如: A 异步操作执行时间为 0~10 秒,B 异步操作执行时间为 5 秒,若 A 操作在 5 秒内完成,则执行 A 的结果,否则执行 B 的结果。
any() 可用于获取加载系统资源。
比如:有多个服务器存放相同的资源,可以使用 any() 通过多个异步操作 分别访问这些服务器,哪个 异步操作 先成功完成,则使用哪个服务器的资源。
【race(): 针对第一个执行完成的异步操作,成功进入 then(),出错进入 catch()】
function promise() {
return new Promise((resolve, reject) => {
//做一些异步操作
setTimeout(function(){
console.log('第一次回调');
reject('第一次回调失败');
}, 1000);
});
} function promise2() {
return new Promise((resolve, reject) => {
//做一些异步操作
setTimeout(function(){
console.log('第二次回调');
resolve('第二次回调成功');
}, 2000);
});
} Promise.race([promise(), promise2()])
.then(function(results) {
console.log('第一次成功')
console.log(results);
}).catch(function(errors) {
console.log('第一次失败')
console.log(errors);
}); 【输出结果:】
第一次回调
第一次失败
第一次回调失败
第二次回调 【any(): 针对第一个执行成功的操作,执行成功进行 then(),全部执行失败则执行 catch()】
function promise() {
return new Promise((resolve, reject) => {
//做一些异步操作
setTimeout(function(){
console.log('第一次回调');
reject('第一次回调失败');
}, 1000);
});
} function promise2() {
return new Promise((resolve, reject) => {
//做一些异步操作
setTimeout(function(){
console.log('第二次回调');
resolve('第二次回调成功');
}, 2000);
});
} Promise.any([promise(), promise2()])
.then(function(results) {
console.log('至少有一个成功')
console.log(results);
}).catch(function(errors) {
console.log('全部失败')
console.log(errors);
}); 【输出结果:】
第一次回调
第二次回调
至少有一个成功
第二次回调成功
二、jQuery 中的 Promise
1、$.Deferred()
(1)简单理解
Deferred 是 jQuery 实现 异步编程的一种解决方案,使用起来与 Promise 类似,也可以实现链式调用。通过 Deferred 对象可以获取异步操作的结果。
(2)直观了解一下?
直接使用 console.dir($.Deferred()) 控制台打印一下 Deferred 对象的属性。
如下图所示,$.Deferred() 返回一个对象,称其为 Deferred 对象,可以看到其内部定义了一些方法:resolve()、reject()、done()、fail() 等。通过这些方法接收、传递 回调函数 从而实现 异步调用。
(3)$.Deferred() 的使用?
使用基本上与 Promise 类似,但是语法糖上有些许差别。
比如:Promise 中的 then()、catch() 与 Deferred 中的 then()、done()、fail() 功能相同。
【常用方法:】
(1)jQuery.Deferred(function) 或者 $.Deferred(function)
创建一个新的 Deferred 对象,function 是可选参数,若存在则在构建 Deferred 对象后执行。 (2)deferred.then(resolve, reject)
异步操作结束后触发,resolve 表示异步操作成功后触发的回调函数, reject 表示异步操作失败后触发的回调函数。 (3)deferred.done()
异步操作成功后触发的回调函数,等同于 then() 中的 resolve。 (4)deferred.fail()
异步操作失败后触发的回调函数,等同于 then() 中的 reject。 (5)deferred.resolve()
手动将 异步操作状态 变为 解决,会立即触发 done()。 (6)deferred.reject()
手动将 异步操作状态 变为 失败,会立即触发 fail()。 (7)deferred.always()
不管异步操作是成功还是失败,均会执行。 (8)deferred.promise()
在原来 deferred 对象的基础上返回一个受限的 deferred 对象。
受限指的是 指开放与 状态变化无关的方法(done、fail 等),不开放 与 状态变化有关的方法(resolve、reject)。
即无法从外部主动改变当前 deferred 对象的状态。
通过上面的方法介绍,可以看到 Deferred 其实用起来与 Promise 很类似。稍稍有些许差别。
比如:Deferred 对象可以直接调用其 resolve、reject 方法直接去改变当前异步操作的状态,当然这样的操作是有风险的,毕竟异步操作有了提前结束的可能。而其调用 promise 方法可以返回一个受限的对象,无法主动结束异步操作,此时的情况就与 Promise 的使用类似了。
【Deferred 用法举例:(调用 promise 方法返回受限的 Deferred 对象)】
function testDeferred() {
let deferred = $.Deferred(); // 创建一个 Deferred 对象 setTimeout(function() {
// 手动改变状态
deferred.resolve("调用成功");
}, 2000); return deferred.promise();
} testDeferred().then(data => {
console.log(data);
}); 【Promise 用法举例:(与上面 Deferred 效果是一致的)】
function testPromise() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
// 手动改变状态
resolve("调用成功");
}, 2000);
});
} testPromise().then(data => {
console.log(data);
}); 【Deferred 用法举例:(Deferred 未受限时,在外部可以直接改变 异步操作状态,提前结束异步操作)】
function testDeferred() {
let deferred = $.Deferred(); // 创建一个 Deferred 对象 setTimeout(function() {
// 手动改变状态
deferred.resolve("调用成功");
}, 2000); return deferred;
} let deferred = testDeferred(); // 获取到 Deferred 对象 deferred.then(function(data) {
// 异步操作成功后执行
console.log(data);
}, function(error) {
// 异步操作失败后执行
console.log(error);
}).always(function() {
// 异步操作成功或者失败都会执行
console.log("总是执行")
}); deferred.reject("执行失败"); // 会直接改变 异步操作状态为 失败
2、$.when()、$.ajax()
(1)$.when()
$.when() 功能与 Promise 中的 all() 方法类似,都是同步执行(触发) 多个异步操作,并在所有异步操作执行完成后才会去 调用回调函数。
不过 all() 接收的参数为 promise 数组,then() 接收的参数为 数组形式。
而 $.when() 接收的参数是多个 Deferred 对象,then() 接收的参数 与 Deferred 结果一一对应。
【$.when() 举例:】
function testDeferred() {
let deferred = $.Deferred(); // 创建一个 Deferred 对象 setTimeout(function() {
// 手动改变状态
deferred.resolve("第一个回调调用成功");
}, 2000); return deferred.promise();
} function testDeferred2() {
let deferred = $.Deferred(); // 创建一个 Deferred 对象 setTimeout(function() {
// 手动改变状态
deferred.resolve("第二个回调调用成功");
}, 3000); return deferred.promise();
} $.when(testDeferred(), testDeferred2()).then(function(data, data2) {
// 异步操作成功后执行
console.log(data);
console.log(data2)
}, function(error) {
// 异步操作失败后执行
console.log(error);
}); 【输出结果:(等待三秒)】
第一个回调调用成功
第二个回调调用成功
(2)$.ajax()
$.ajax() 可以理解成一个 受限的 Deferred 对象,也即前面提到的 不能改变异步状态的 Deferred 对象(没有 resolve、reject 方法)。
虽然使用起来与 Deferred 类似,但是 $.ajax() 语法糖还是有些许不同。比如:ajax 中的 success、error、complete 方法分别对应 deferred 中的 done、fail、always 方法。
【基本写法:】
$.ajax({
url: "https://www.cnblogs.com/l-y-h/",
success: function(data) {
console.log("成功");
console.log(data);
},
error: function(error) {
console.log("失败");
console.log(error)
},
complete: function() {
console.log("总是执行");
}
}) 【链式写法:】
$.ajax("https://www.cnblogs.com/l-y-h/").success(function(data) {
console.log("成功");
console.log(data);
}).error(function(error) {
console.log("失败");
console.log(error)
}).complete(function() {
console.log("总是执行");
}); 【链式写法:(等同于上一种链式写法)】
$.ajax("https://www.cnblogs.com/l-y-h/").done(function(data) {
console.log("成功");
console.log(data);
}).fail(function(error) {
console.log("失败");
console.log(error)
}).always(function() {
console.log("总是执行");
});
关于 Promise 的一些简单理解的更多相关文章
- 【原创】分布式之数据库和缓存双写一致性方案解析(三) 前端面试送命题(二)-callback,promise,generator,async-await JS的进阶技巧 前端面试送命题(一)-JS三座大山 Nodejs的运行原理-科普篇 优化设计提高sql类数据库的性能 简单理解token机制
[原创]分布式之数据库和缓存双写一致性方案解析(三) 正文 博主本来觉得,<分布式之数据库和缓存双写一致性方案解析>,一文已经十分清晰.然而这一两天,有人在微信上私聊我,觉得应该要采用 ...
- 简单理解ECMAScript2015中的箭头函数新特性
箭头函数(Arrow functions),是ECMAScript2015中新加的特性,它的产生,主要有以下两个原因:一是使得函数表达式(匿名函数)有更简洁的语法,二是它拥有词法作用域的this值,也 ...
- 【javascript】Promise/A+ 规范简单实现 异步流程控制思想
——基于es6:Promise/A+ 规范简单实现 异步流程控制思想 前言: nodejs强大的异步处理能力使得它在服务器端大放异彩,基于它的应用不断的增加,但是异步随之带来的嵌套.难以理解的代码让 ...
- generator 到 async 的简单理解。
generator 到 async 的简单理解.觉得实现方式很有意思. 1. generator generator 函数返回一个遍历器对象 遍历器对象 每次调用next 方法 返回 有着value ...
- git的简单理解及基础操作命令
前端小白一枚,最近开始使用git,于是花了2天看了廖雪峰的git教程(偏实践,对于学习git的基础操作很有帮助哦),也在看<git版本控制管理>这本书(偏理论,内容完善,很不错),针对所学 ...
- 大白话讲解Promise(二)理解Promise规范
上一篇我们讲解了ES6中Promise的用法,但是知道了用法还远远不够,作为一名专业的前端工程师,还必须通晓原理.所以,为了补全我们关于Promise的知识树,有必要理解Promise/A+规范,理解 ...
- 简单理解Struts2中拦截器与过滤器的区别及执行顺序
简单理解Struts2中拦截器与过滤器的区别及执行顺序 当接收到一个httprequest , a) 当外部的httpservletrequest到来时 b) 初始到了servlet容器 传递给一个标 ...
- [转]简单理解Socket
简单理解Socket 转自 http://www.cnblogs.com/dolphinX/p/3460545.html 题外话 前几天和朋友聊天,朋友问我怎么最近不写博客了,一个是因为最近在忙着公 ...
- Js 职责链模式 简单理解
js 职责链模式 的简单理解.大叔的代码太高深了,不好理解. function Handler(s) { this.successor = s || null; this.handle = funct ...
随机推荐
- 单线程模式从网易下载A股叁仟捌佰支股票一年的交易数据耗时十四分钟
代码下载:https://files.cnblogs.com/files/xiandedanteng/StockDataDownloader20200305.rar 压缩包内包含股票代号文件,调整好日 ...
- Oracle SQL 判断某表是否存在
SQL> SELECT COUNT (*) as cnt FROM ALL_TABLES WHERE table_name = UPPER('your_table'); CNT -------- ...
- Java高并发系列——检视阅读
Java高并发系列--检视阅读 参考 java高并发系列 liaoxuefeng Java教程 CompletableFuture AQS原理没讲,需要找资料补充. JUC中常见的集合原来没讲,比如C ...
- 蓝奏网盘API
蓝奏云网盘API 2.0 基于Python3实现,最强的蓝奏云API~ 蓝奏云注册 更新说明 修复了登录时 formhash 错误的问题 解决了多次上传大文件被限制的问题 #3 细化 API 接口的功 ...
- 安装python3,配置pycharm
1.下载最新版python3 首先去python官网下载python3的源码包,网址:https://www.python.org/ 进去之后点击导航栏的Downloads,也可以鼠标放到Downlo ...
- Javaweb中的请求路径的相关总结
重定向和转发相对路径和绝对路径问题 注意:转发和重定向的URLString前有加 / 为绝对路径 反之为相对路径 1.假设通过表单请求指定的Url资源 action="LoginServ ...
- in多值优化
〇.问题 今天ocp群里有人问 SELECT * FROM table WHERE id IN(11,2,3,44,...) 在in里面有大量数据4000+,有什么 好的处理方式吗? 我的优化方案的总 ...
- 正则表达式与SQL
在我心中正则表达式和SQL就是一样的东西. SQL是结构化查询语言,是根据某个查询.修改规则来查询修改数据,是描述一个规则给数据库,数据库来执行, 数据库返回结果,过程不需要考虑,不算是编程语言. 正 ...
- Java线程阻塞方法sleep()和wait()精炼详解
版权声明:因为个人水平有限,文章中可能会出现错误,如果你觉得有描述不当.代码错误等内容或者有更好的实现方式,欢迎在评论区告诉我,即刻回复!最后,欢迎关注博主!谢谢 https://blog.csdn. ...
- 五分钟带你读懂 TCP全连接队列(图文并茂)
爱生活,爱编码,微信搜一搜[架构技术专栏]关注这个喜欢分享的地方. 本文 架构技术专栏 已收录,有各种视频.资料以及技术文章. 一.问题 今天有个小伙伴跑过来告诉我有个奇怪的问题需要协助下,问题确实也 ...