Promise (1) 初步接触
最近看了《javascript Promise迷离书》,对Promise的理解颇有加深。那么就从总结Promise开始吧。
1 什么是Promise?
抽象描述: Promise是一个规范,提供了一套定义用来与 一个可能会在任意时刻完成或失败的异步过程的结果对象交互的接口。( Promise提供了一套接口,用来与异步过程的结果的对象 交互。这样读着好(bing)理(mei)解(you)点)
简单描述:Promise是一个异步对象,在Promise对象创建时可能是未知的,当状态变换后Promise表示一个异步操作的最终结果,与之进行交互的方式主要是 then
方法,该方法注册了两个回调函数,它允许你为异步代码执行结果的成功和失败分别绑定相应的处理方法(handlers ),用于处理resolve的value(常用变量名)或reject 的reason(常用变量名)。
// 在Promise对象创建时可能是未知的,
var promise = new Promise(function(resolve,reject){
if (Math.random() > 0.5)
// 状态变换~~Promise表示一个异步操作的最终结果 成功
resolve('value resolve');
else {
// 状态变换~~Promise表示一个异步操作的最终结果 失败
reject('reason reject');
}
});
// 与之进行交互的方式主要是 then 方法,该方法注册了两个回调函数
promise.then(function(value){
// 处理 异步代码执行成功的结果
console.log(value);
}).catch(function(error){
// 处理 异步代码执行失败的结果
console.error(error);
});
1.1 Promise对象的创建
new Promise构造器之后,会返回一个promise对象,创建Promise对象的基本语法:
// new Promise(executor);
// 给Promise构造函数传递的参数是一个函数
// 函数接受两个参数resolve, reject, new Promise(function(resolve, reject) {
// 异步处理的到结果后、判定什么情况下resolve这个结果,什么情况下reject这个结果.
// 通常resolve(value),会将promise的状态转变成Fulfilled
// 通常reject(reason),会将promise的状态转变为Rejected
if ( /* 异步操作成功的判定条件 */ ) {
resolve(value);
} else {
reject(reason);
}
});
new一个Promise对象,给Promise构造函数传递的参数是一个函数Fn(Promise构造函数只接受函数作为参数)。该函数Fn接受两个参数resolve和 reject。
那么Promise 内部实现中会给这个函数Fn传递的参数具体是两个什么方法了?
代码参考es6-promise
function Promise(resolver) {
this[PROMISE_ID] = nextId();
this._result = this._state = undefined;
this._subscribers = []; if (noop !== resolver) {
typeof resolver !== 'function' && needsResolver();
this instanceof Promise ? initializePromise(this, resolver) : needsNew();
}
} function initializePromise(promise, resolver) {
try {
resolver(function resolvePromise(value) {
_resolve(promise, value);
}, function rejectPromise(reason) {
_reject(promise, reason);
});
} catch (e) {
_reject(promise, e);
}
}
比对着图看更清晰
那么内部执行resolve和执行reject主要做了什么事情了?先从表象推结果
如下图创建的testResolvePromise,在Promise构造函数接受的函数内部执行了 resolve。
testResolvePromise对象的[[PromiseStatus]]值是"resolved",[[PromiseValue]]值"resolve: value"(这个字符串就是我们传入resolve函数的值)。
对比testRrejectPromise,testRrejectPromise对象的[[PromiseStatus]]值是"rejected",[[PromiseValue]]值"reject:reason"(这个字符串就是我们传入reject函数的值)。
(1)resolve和reject的调用改变了promise的状态。
(2)同时将处理后的值赋给了promise对象的[[PromiseValue]]属性(为什么是处理后,见后文。如果你resolve的value值是一个promise对象,那么就不是简单的赋值了。
总结:给Promise构造函数传递的参数是一个函数,且该函数接受的两个参数也是函数,由Promise的内部实现,用以改变创建的promise对象的状态。
1.2 Promise的状态
用new Promise 实例化的promise对象有三个状态: pending,fulfilled,rejected。
pending: promise对象刚被创建的初始状态,既未完成也没有失败的状态,此状态可以迁移至fulfilled和rejected状态。
fulfilled:意味着操作成功完成,resolve(成功)时,此时的状态不能迁移(不能改变的)。
rejected:意味着操作失败reject(失败)时,此时的状态不能迁移(不能改变的)。
eg: 如图创建了一个testPromisePending对象,用setTimeout设置一个时间10秒后再执行resolve。在这时间段前还没有执行resolve,此时的testPromisePending的[[PromiseStatus]]值是"pending"。10秒过后再在控制太输出一次testPromisePending,此时它的状态就迁移了[[PromiseStatus]]值是"resolved",即是fulfilled状态。
“promise对象的状态,从Pending转换为Fulfilled或Rejected之后, 这个promise对象的状态就不会再发生任何变化”,--《javascript Promise迷离书》(书中的流程图十分的清晰,有利于promise的工作流程理解)
pending状态---->resolve(value)----->fulfilled状态.
pending状态---->reject(reason)----->rejected状态.
总结:如果一个promise不是pending状态,就说明这个promise是settled(不变的),它要么fulfilled状态要么是rejected状态。
1.3 Promise的then方法
Promise构造函数接受一个函数作为参数,函数里面内部代码执行了resolve或reject。resolve(value)或 reject(reason)传递了值后续怎么处理?这时候then方法闪亮登场了,promise的then方法里面可以处理resolve(value) 或reject(reason)时得到的值。then方法接受两个可选参数。
var testPromise = new Promise(function(resolve, reject) {
if ( /* 异步操作成功的判定条件 */ ) {
resolve(value);
//函数内部resolve了value值,那么我们怎么处理value值了;
} else {
reject(error);
//函数内部reject了reason值,那么我们怎么处理reason值了;
}
});
testPromise.then(function onFulfilled(value) {
//当testPromise的状态是fulfilled的时候执行
}, function onRejected(reson) {
//当testPromise的状态是rejected的时候执行
})
pending状态---->resolve(value)----->fulfilled状态---->执行then方法里面onFulfilled(value)方法
pending状态---->reject(reason)----->rejected状态---->执行then方法里面onRejected(reason)方法
当testPromise的状态迁移成 fulfilled或rejected的时候时才执行后续的then方法。(两条路线只会执行一条,)
testPromise的状态是fulfilled就执行onFulfilled方法,此时的value参数的值就是之前resolve的值,onFulfilled函数内部就对传递进来value值进行后续的处理了。
testPromise的状态是rejected就执行onRejected方法,此时的reason参数的值就是之前reason的值,onRejected函数内部就对传递进来reason值进行后续的处理。
then方法接受两个可选参数,具体又干了什么了?
代码参考es6-promise
假设目前代码逻辑是
// 在Promise对象创建时可能是未知的,
var promise = new Promise(function(resolve,reject) {
setTimeout(function() {
resolve(Math.random());
})
});
var bPromise = promise.then(function(value) {
console.log(value);
})
then执行完后返回的结果还是一个promise对象,这个新创建的promise跟then方法执行的回调onFulfilled或者onRejected有关系(废话)。
⑴.调用then方法会返回的promise是新创建的
⑵.这个新创建promise的值跟前一个promise的onFulfilled和onRejected的函数内部有无return 以及return的值有关系。如果没有return则返回一个状态为fulfilled的或者rejected,[[PromiseValue]](不同实现内部属性不一定叫PromiseValue)为undefined的promise对象。
⑶.承接⑵如果有return,retuen 一个普通的object对象那么新创建promise对象的 [[PromiseValue]] 的值就等于 object。 如果 return 的是一个Promise对象,还是会返回一个新的promise对象,且属性值[[PromiseValue]]和[[PromiseStatus]]与return 的Promise对象属性值一样
⑷.如果有return,retuen 一个普通的thenable对象,会先执行thenable对象的then方法,得到[[PromiseValue]]和[[PromiseStatus]],然后将值赋给新的promise对象(下一篇解释)
返回了一个普通的promise,imANewPromiseB长什么样子
var testPromiseA = new Promise(function(resolve, reject) {
resolve("testPromiseA")
});
var testForreturnPromise = new Promise(function(resolve, reject) {
resolve("just test for testPromiseB")
});
//返回了一个普通的promise,imANewPromiseB长什么样子
var imANewPromiseB = testPromiseA.then(function onFulfilled(value) {
//返回了一个promise
return testForreturnPromise
})
// 在控制台输出,imANewPromiseB和 testForreturnPromise 的属性值一样,但他们不是同一个promise。
//imANewPromiseB 是一个新的promise 对象
在控制台输出,imANewPromiseB和 testForreturnPromise 的属性值一样,但他们不是同一个promise,且imANewPromiseB和testPromiseA也不是同一个promise 对象。imANewPromiseB是一个新的promise。
总结:每次调用then都会返回一个新创建的promise对象。
1.4 为啥要用Promise?
有一个作用就是把代码从异步回调函数的逻辑混乱拯救出来(虽然还是要用回调但是已经比之前好了,期待async/await用起来更顺畅),让代码看起来逻辑清晰可爱。因为Promise把异步处理对象和处理规则进行规范化。
function getJSON(url) {
return new Promise(function(resolve, reject) {
let xhr = new XMLHttpRequest(); //神奇的对象
xhr.open('GET', url);
xhr.onreadystatechange = handler;
// 无论readyState值何时发生改变,XMLHttpRequest对象都会激发一个readystatechange事件,handler被调用,然后根据结果resolve,或者reject
xhr.responseType = 'json';
xhr.setRequestHeader('Accept', 'application/json');
xhr.send();
function handler() {
if (this.readyState === this.DONE) {
if (this.status === 200) {
resolve(this.response);
//successDo(this.response)
} else {
reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']'));
//faileDo(this.response)
}
}
};
});
}
// 使用promise
getJSON(url).then(function onFulfilled(value) {
//successDo
}, function onRejected(reson) {
//faileDo
})
// 使用回调
getJSON(url, successDo, faileDo)
使用回调的方式来做getJSON,拿数据和对拿到数据成功和失败都在一个函数里面操作处理。
使用promise来做getJSON,getJSON只需要做好自己拿数据,且通过resolve和reject返回拿数据成功还是失败的逻辑,并不关心成功或失败的处理。后续的then方法会根据getJSON返回的promise的状态执行onFulfilled方法或者onRejected方法来处理,数据拿成功或者数据拿失败的逻辑。这个流程比较符合人类(我这种人类)的习惯,一步一步执行操作,拿数据返回成功或失败----->处理拿数据成功或失败。
回到文章的抽象描述,Promise处理了异步操作的结果,并提供了规范的接口让你与之交互。
总结:Promise 可以让异步处理对象更像一个流程操作。使用Promise 的时候要思考Promise 的适用场景。并不是说在异步处理的时候Promise永远都是最好的选择。
总结
本章Promise的描述是按照:new一个promise对象,给Promise构造函数传递的参数是一个函数。函数内部执行resolve或reject使promise对象的状态从pending状态迁移到fulfilled状态或者rejected状态,状态迁移后就不会再改变。(后续会说明为什么promise对象需要状态)。promise对象状态迁移成 fulfilled或rejected的时候时才执行后续的then方法,状态是fulfilled就执行then里面的onFulfilled方法,状态是rejected就执行then里面onRejected方法。then执行完后返回的是一个新的promise对象(后续会说明为什么它会返回一个新的promise对象)。
文中例子比较粗糙,理解不准确之处,还请教正。
第一次修正于(2019/03/31)
Promise (1) 初步接触的更多相关文章
- php大力力 [006节]初步接触认识phpMyAdmin
phpMyAdmin 2015-08-22 php大力力006. 初步接触认识phpMyAdmin 以下是phpAdmin网络截图: 这是通过MAMP一键安装的. php中MyAdmin的使用-猿代码 ...
- avalon - 初步接触
avalon - 初步接触 avalon的介绍http://rubylouvre.github.io/mvvm/ 按照作者的介绍,在HTML中添加绑定,在JS中用avalon.define定义View ...
- 初步接触CERNVM
初步接触的来源是对ROOT数据分析工具的搜索,看到一个叫做Life as a Physicist的国外博客.知道了这个包含容器分发的软件,跟重要的是,这个欧洲核子中心开发的平台,对于我等科研人员是一大 ...
- Spring boot -环境搭建 ,初步接触(1)
1. Eclipse 创建 maven project 项目目录如下: 2. pom.xml 配置文件 <project xmlns="http://maven.apache.or ...
- 为什么要使用puppet 及初步接触
为什么要使用puppet 及初步接触 1.简介 云计算环境下,密度高,机器数量多,还要求弹性和伸缩性,这对于运维提出更高的要求.系统管理员需要经常安装操作系统,对系统参数进行配置和优化,对人员进行 ...
- C#初步接触
如同非常多刚開始学习的人一样,刚接触C#的时候,也是一头雾水,学习了好长时间,都搞不清楚一些基本名称是什么.什么是C#?什么是.net?什么是visual studio?它们之间有什么关系?以下我们就 ...
- 初步接触html心得
接触HTML大概有七天,做一下小总结,过过记忆. html大致可分为三部分:Dtd头.Head.Body三大部分. Dtd头:是用于浏览器编辑的,也就是俗话说的给电脑看的的东西. Head:内细分下大 ...
- 实验记录一 初步接触cortex-M3
应该说老早就在接触cortex-M3了.曾经没想到会接触嵌入式,结果由于导师的缘故.在选择项目管理时,就呵呵了.不废话.搭配环境非常easy,纯粹傻瓜式.可由于自己的马虎,却让自己一直困惑. 记得在前 ...
- java_web学习(四) 二维表的制作(初步接触MVC)
我们需要做一个jsp页面,动态显示信息表的内容. 一.需求分析 1. 做一个实体类:StudentInfo (包含4个字段) 2. 如图模拟生成3条数据,本质上就是new StudentInfo ...
随机推荐
- Apache URL重写规则
1.简介 Apached的重写功能,即是mod_rewrite模块功能,它是apache的一个模块.它的功能非常强大,可以操作URL中的所有部分. 因此我们就可以改写url,给用户提供一个简介大方的u ...
- Mac下安装BeautifulSoup
1.输入命令su,输入root密码,进入root用户 2.执行命令安装pip: sudo easy_install pip 详细pip介绍:http://blog.csdn.net/olanlanxi ...
- JavaScript中的数据结构及实战系列(1):队列
开题 张三丰教无忌太极剑法: 还记得吗? 全都记得. 现在呢? 已经忘却了一小半. 啊,已经忘了一大半. 不坏不坏,忘得真快,那么现在呢? 已经全都忘了,忘得干干净净. 好了,你上吧. 长时间写前端代 ...
- 1.6 OWIN集成
OWIN集成 安装 使用 如果在应用程序里既使用ASP.NET MVC也使用ASP.NET Web API,需要在工程里安装Abp.Owin包. 安装 添加Abp.Owin包到主工程里(一般是web工 ...
- 【渗透测试】hydra使用小结
-R:继续从上一次进度接着破解 -S:大写,采用SSL链接 -s <PORT>:小写,可通过这个参数指定非默认端口 -l <LOGIN>:指定破解的用户,对特定用户破解 -L ...
- DELL Precision Tower7910重装系统+开机出现GRUB界面如何处理
想给实验室的工作站重新装个Win7系统,因为以前并没装过工作站的系统,发现和普通的电脑装系统还是有些不一样的.主要的问题就在于主板的不同. 尝试了老毛桃U盘启动盘安装,结果在WinPE里面提示找不到硬 ...
- python编程总结
1.signal.signal(signal.SIGCHLD,signal.SIG_IGN) 这句话的作用是 防止产生僵尸进程.详细解释看 http://www.cnblogs.com/Anker/p ...
- GitHub 入门不完全指南(未完待续)
我一直认为 GitHub 是一座宝藏,想让更多人的知道它.加入到这个社区中.本人能力有限,如果文中出现不对的地方,欢迎指正交流. 一.前言 大家好,我是削微寒(xuē wēi hán),一个走在进阶路 ...
- 蓝桥杯-马虎的算式-java
/* (程序头部注释开始) * 程序的版权和版本声明部分 * Copyright (c) 2016, 广州科技贸易职业学院信息工程系学生 * All rights reserved. * 文件名称: ...
- selenium IDE的3种下载安装方式
第一种方式: 打开firefox浏览器-----点击右上角-----附加组件----插件----搜索框输入“selenium”-----搜索的结果中下拉到页面尾部,点击“查看全部的37项结果”---进 ...