一、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 的一些简单理解的更多相关文章

  1. 【原创】分布式之数据库和缓存双写一致性方案解析(三) 前端面试送命题(二)-callback,promise,generator,async-await JS的进阶技巧 前端面试送命题(一)-JS三座大山 Nodejs的运行原理-科普篇 优化设计提高sql类数据库的性能 简单理解token机制

    [原创]分布式之数据库和缓存双写一致性方案解析(三)   正文 博主本来觉得,<分布式之数据库和缓存双写一致性方案解析>,一文已经十分清晰.然而这一两天,有人在微信上私聊我,觉得应该要采用 ...

  2. 简单理解ECMAScript2015中的箭头函数新特性

    箭头函数(Arrow functions),是ECMAScript2015中新加的特性,它的产生,主要有以下两个原因:一是使得函数表达式(匿名函数)有更简洁的语法,二是它拥有词法作用域的this值,也 ...

  3. 【javascript】Promise/A+ 规范简单实现 异步流程控制思想

    ——基于es6:Promise/A+ 规范简单实现 异步流程控制思想  前言: nodejs强大的异步处理能力使得它在服务器端大放异彩,基于它的应用不断的增加,但是异步随之带来的嵌套.难以理解的代码让 ...

  4. generator 到 async 的简单理解。

    generator 到 async 的简单理解.觉得实现方式很有意思. 1. generator generator 函数返回一个遍历器对象 遍历器对象 每次调用next 方法 返回 有着value ...

  5. git的简单理解及基础操作命令

    前端小白一枚,最近开始使用git,于是花了2天看了廖雪峰的git教程(偏实践,对于学习git的基础操作很有帮助哦),也在看<git版本控制管理>这本书(偏理论,内容完善,很不错),针对所学 ...

  6. 大白话讲解Promise(二)理解Promise规范

    上一篇我们讲解了ES6中Promise的用法,但是知道了用法还远远不够,作为一名专业的前端工程师,还必须通晓原理.所以,为了补全我们关于Promise的知识树,有必要理解Promise/A+规范,理解 ...

  7. 简单理解Struts2中拦截器与过滤器的区别及执行顺序

    简单理解Struts2中拦截器与过滤器的区别及执行顺序 当接收到一个httprequest , a) 当外部的httpservletrequest到来时 b) 初始到了servlet容器 传递给一个标 ...

  8. [转]简单理解Socket

    简单理解Socket 转自 http://www.cnblogs.com/dolphinX/p/3460545.html  题外话 前几天和朋友聊天,朋友问我怎么最近不写博客了,一个是因为最近在忙着公 ...

  9. Js 职责链模式 简单理解

    js 职责链模式 的简单理解.大叔的代码太高深了,不好理解. function Handler(s) { this.successor = s || null; this.handle = funct ...

随机推荐

  1. python中函数的参数:必传参数(位置参数)、默认值参数、参数组传参、关键字传参

    1.必传参数也叫做位置参数,因为必填,也必须对应位置 2.默认值参数如上图的word 3.参数组参数:传进去的是0个.或多个value的形式,,,和位置参数有点像,只传value值,但是没有限制个数 ...

  2. pytest(3):pytest运行参数介绍

    前言 pytest 带有很多参数,可以使用 pytest --help  来查看帮助文档,下面介绍几种常用的参数: 无参数 读取路径下所有符合规则的文件,类,方法,函数全部执行.使用方法如下:  py ...

  3. 三、spring boot开发web应用-使用传统的JDBC

    上一节<spring boot第一个web服务>中我们只是简单的展示了spring mvc的功能,并没有涉及到具体的CRUD的操作,也没有涉及到数据持久化的方面.本节中我们将基于原始的JD ...

  4. [LeetCode]67. 二进制求和(字符串)(数学)

    题目 给你两个二进制字符串,返回它们的和(用二进制表示). 输入为 非空 字符串且只包含数字 1 和 0. 题解 两个字符串从低位开始加,前面位不够补0.维护进位,最后加上最后一个进位,最后反转结果字 ...

  5. [LeetCode]1083. 销售分析 II(Mysql,having+if)

    题目 编写一个 SQL 查询,查询购买了 S8 手机却没有购买 iPhone 的买家. 题解 使用having + sum+if,而不是自查询. 代码 # Write your MySQL query ...

  6. 关于java for循环常见练习题

    使用for循环方式计算2+4+6+…+100的值 package day02; /** * 使用for循环方式计算2+4+6+…+100的值 * @author mu * */ public clas ...

  7. k8s架构分析(二)

    master节点 k8s的集群由master和node组成,节点上运行着若干k8s服务. master节点之上运行着的后台服务有kube-apiserver .kube-scheduler.kube- ...

  8. 云计算openstack核心组件——neutron网络服务(8)

    一.neutron 介绍:   Neutron 概述 传统的网络管理方式很大程度上依赖于管理员手工配置和维护各种网络硬件设备:而云环境下的网络已经变得非常复杂,特别是在多租户场景里,用户随时都可能需要 ...

  9. OKR工作法

    OKR 目标与关键成果 [Objectives and Key Results] OKR是目标管理工具,它的价值不在于是否完成OKR,而在于呈现出团队最应该关注的事情,通过持续性的沟通确保每个人都聚焦 ...

  10. turtle空间坐标系

    利用空间坐标改变行进 以海龟的角度来看,无论往那个方向运行,都叫正方向 turtle.fd向海龟的正前方运行,turtle.bk向反方向运行