Promise初级教程2:详细介绍

上一篇简单介绍了 Promise 的外形,让大家了解 Promise 是什么样的,可以用在哪里,有个心理准备。不然突然跳出个恐龙出来,吓你们一跳,233。
其实这篇文章才算正式介绍 Promise,咳咳...

本来想周末写的,不过遇到了广东百年难遇的下雪天气,一直窝在床上发抖,所以拖到现在(好吧,其实是懒)。

Promise 已经有很多人介绍了,这里安利一些比较值得看的文章,不妨先看看。

估计上面的看完,对 Promise 的大概都懂了,那接下来的内容,理论上可以不用看了,因为上面的已经说的很全很详细了~~不过,我还是按个人理解,整理一下。

1. Promise 定义

1.1 是什么

Promise 是抽象异步处理对象以及对其进行各种操作的组件 -- 来自 JavaScript Promise迷你书

1.2 规范

ES6 的[Promise][0]是遵循 Promises/A+ 规范的。Promises/A+ 一开始是社区规范,然后大部分的 Promise 类库遵循了这个规范,所以渐渐变成了一个标准。
所以我们可以通过 Promises/A+ 的规范去学习 Promise。

Promises/A+中文版翻译在这里,英文看不太懂可以看看翻译版本。

1.3 状态

每个 Promise 对象有一种状态:Pending(等待)、Resolved/Fulfilled(成功)和 Rejected(失败)中的一种,变化过程如下:

promise_states.png

如上图,状态只能从 Pending 变为 Resolved 或者 Rejected 两者之中的一种,过程不可以逆转

2. Promise 用法

这里主要介绍 Promise 的一些方法。

2.1 new Promise

通过 new,新建 Promise 对象,这是最常规的用法。看上一篇文章的例子:

// ex1
var startTime = Date.now();
console.log('start', startTime);

var handler = new Promise(function (resolve, reject) {
    setTimeout(function () {
        resolve('done', Date.now() - startTime);
    }, 500);
}); 

handler.then(function (val) {
    console.log('then resolve', val, Date.now() - startTime);
}, function (e) {
    console.log('then reject', e, Date.now() - startTime);
});

结果很明显,是进入成功承诺的回调,这里没什么好说的。

2.2 Promise.resolve

静态方法 Promise.resolve(value)new Promise() 的快捷方式。

Promise.resolve(42); 

// 等价于
new Promise(function(resolve){
    resolve(42);
});

问题:参数 value 可以是什么?

  1. 空。
  2. 普通的对象。
  3. 带有 then 方法的不标准 Promise 对象。
  4. Promise 对象。

下面让我们一个个来验证,下面结果是在 chrome & firefox 里的调试结果。

2.2.1 空

如果不传参数,则返回一个 value 为 undefined 的 Promise 对象。

// value 为空,返回的Promise对象value为undefined
Promise.resolve() 
// chrome  --> Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: undefined}
// firefox --> Promise { <state>: "fulfilled", <value>: undefined }

2.2.2 普通的对象

如果参数为普通对象,返回状态为 Resolve 的 Promise 对象,其 value 为传人的参数。

// value 为string
Promise.resolve('42') 
// chrome  --> Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: "42"}
// firefox --> Promise { <state>: "fulfilled", <value>: "42" }

// value 为 object
Promise.resolve({a:1}) 
// chrome  --> Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: Object}
// firefox --> Promise { <state>: "fulfilled", <value>: Object }

2.2.3 带有 then 方法的不标准 Promise 对象

如果参数为带有then方法的不标准 Promise 对象,返回新的 Promise 对象。我们先来看 jQuery 的 $.ajax 方法返回的 deferred 对象。
页面引入 jQuery-2.1.4,如下:

ajax.png

通过上图,我们可以知道 deferred 对象是 thenable(包含了then方法的对象或函数)对象,但不是标准的 Promise 对象(怎样才算标准的Promise对象?)。

那看看通过 Promise.resolve 转换效果,先看请求成功的返回结果,可以看到 [[PromiseValue]] 就是 $.ajax 的 deffered 对象,如下:

promise_resolve_1.png

再看看请求错误的时候结果,如下:

promise_resolve_2.png

有没有看出什么?是不是成功或失败,返回 Promise 对象的状态都是 resolved ?那是不是都进入 resolved 的回调?想一下好像是的哦,让我们来验证下~

先看请求成功的时候,其实没什么悬念:

var handler = Promise.reject($.ajax('/stock/chart/1414672685/position'));
handler.then(function (val) {
    console.log('resolve', val);
}, function (e) {
    console.log('rejected', e);
});
promise_resolve_3.png

再看看请求错误的时候:

var handler = Promise.reject($.ajax('/stock/chart/1414672685/position123'));
handler.then(function (val) {
    console.log('resolve', val);
}, function (e) {
    console.log('rejected', e);
});
promise_resolve_4.png

纳尼,为什么进入 rejected 的回调了?不是说好 Promise 的状态,一旦从 Pending 到 Resolved 或 Rejected 就不能改变了吗?你们说这是为什么?

有人说,看上面两张图中 Promise 打印出来的状态是 Pending,秘密所在。咳咳,其实 Promise.then 方法会返回一个新的 Promise 对象,方便链式调用而已。

其实这个问题与 Promise 的解析流程有关,我们待会再看这个问题~

2.2.4 Promise 对象

如果参数为 Promise 对象,直接返回该 Promise 对象:

var promise1 = new Promise(function (resolve, rejected) {
    resolve('done')
});
Promise.resolve(promise1);

var promise1 = new Promise(function (resolve, rejected) {
    rejected('fail')
});
Promise.resolve(promise1);  
promise_resolve_5.png

2.2 Promise.reject

Promise.reject(error) 是和 Promise.resolve(value) 类似的静态方法,是 new Promise() 方法的快捷方式。

Promise.reject(new Error("出错了"))

// 等价于
new Promise(function(resolve,reject){
    reject(new Error("出错了"));
});

正常来说,Promise.reject 的参数都应该是error对象,这个没什么好说的。

2.3 Promise.then

Promise.then 是 Promise 里比较重要的一个方法,在 Promises/A+ 规范中,很大部分的篇幅就介绍这个方法。

then 方法,第一个参数是 Resolved 状态的回调函数,第二个参数(可选)是 Rejected 状态的回调函数,调用后返回新的 Promise 对象。就这么看,是不是完了?

我们主要关注下面的两点:

  1. 规范。
  2. 返回新 Promise 对象的状态是怎么决定的。

2.3.1 规范

Promise.then(onFulfilled, onRejected)

onFulfilled & onRejected 的特点:

  • 可选。
  • 必须是函数,不然忽略。
  • onFulfilled 必须在 Promise 的 Resolve 状态后调用,Promise 的 value 为其第一个参数,只能被调用一次。
  • onRejected 必须在 Promise 的 Rejected 状态后调用,Promise 的 reason 为其第一个参数,只能被调用一次。
  • 当成函数般调用(this为undefined)。

对于一个Promise , then 可以被调用多次:

  • 当 Promise fulfilled 后,所有 onFulfilled 都必须按照其注册顺序执行。
  • 当 Promise rejected 后,所有 OnRejected 都必须按照其注册顺序执行。

2.3.2 返回新 Promise

promise2 = promise1.then(onFulfilled, onRejected);
  • 如果 onFulfilled 或 onRejected 返回了值 x, 则执行 Promise 解析流程 [[Resolve]](promise2, x)
  • 如果 onFulfilled 或 onRejected 抛出了异常 e, 则 promise2 应当以 e 为 reason 被拒绝。
  • 如果 onFulfilled 不是一个函数且 promise1 已经 fulfilled,则 promise2 必须以 promise1 的值 fulfilled。
  • 如果 OnReject 不是一个函数且 promise1 已经 rejected, 则 promise2 必须以相同的 reason 被拒绝。

好吧,上面这些都是照搬规范的东西而已(我只是个搬运工,哭),基本上看一遍就知道怎么回事了。

2.4 Promise.catch

Promise.catch 只是 Promise.then(undefined, onRejected) 方法的一个别名。

这里盗个例子:

function taskA() {
    console.log("Task A");
}
function taskB() {
    console.log("Task B");
}
function onRejected(error) {
    console.log("Catch Error: A or B", error);
}
function finalTask() {
    console.log("Final Task");
}

var promise = Promise.resolve();
promise
    .then(taskA)
    .then(taskB)
    .catch(onRejected)
    .then(finalTask);
promise-then-catch-flow.png

特点:

  • 错误具有冒泡性,一直到捕获为止。
  • IE8 下 promise.catch 有保留字问题,用 promise["catch"] 则没问题。
  • 如果没有发生错误,catch会自动被忽略。

2.5 Promise.all

Promise.all 接收一个 promise 对象的数组作为参数,返回一个新的 promise 对象,特点如下:

  • 当数组内所有 promise 对象的状态为 Resolve,其状态才为 Resolve。
  • 当数组内有一个 promise 对象的状态为 Rejected,其状态就为 Rejected(这点promise迷你书中的说法是有误的)。

看测试代码:

// 第一种情况
var promise1 = Promise.resolve(1);
var promise2 = new Promise(function (resolve, reject) {
    setTimeout(function () {
        resolve('error');
    }, 1000);
});
var promise3 = Promise.all([promise1, promise2]);
promise3.then(function (val) {
    console.log('resolve', val);
}, function (e) {
    console.log('reject', e);
});
// resolve [1, "error"]

// 第二种情况
var promise1 = Promise.reject(1);
var promise2 = new Promise(function (resolve, reject) {
    setTimeout(function () {
        resolve('error');
    }, 1000);
});
var promise3 = Promise.all([promise1, promise2]);
promise3.then(function (val) {
    console.log('resolve', val);
}, function (e) {
    console.log('reject', e);
});
// reject 1

2.6 Promise.race

Promise.all 的相对方法,只要数组内有一个 promise 对象的状态改变,其状态就改变。

看测试代码:

var promise1 = Promise.resolve(1);
var promise2 = new Promise(function (resolve, reject) {
    setTimeout(function () {
        resolve('error');
    }, 1000);
});
var promise3 = Promise.race([promise1, promise2]);
promise3.then(function (val) {
    console.log('resolve', val);
}, function (e) {
    console.log('reject', e);
});
// resolve 1

到这里,Promise 的方法已经介绍完了。然而上面还有个问题没解决,让我们来看下 Promise 的解析流程。

本来想接着写的,不过真的太长了,怕大家看的头晕。

所以,有兴趣的话,请看下一篇 Promise 的解析流程

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,175评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,674评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,151评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,597评论 1 269
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,505评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,969评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,455评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,118评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,227评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,213评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,214评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,928评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,512评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,616评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,848评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,228评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,772评论 2 339

推荐阅读更多精彩内容

  • 00、前言Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区...
    夜幕小草阅读 2,124评论 0 12
  • Promiese 简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果,语法上说,Pr...
    雨飞飞雨阅读 3,348评论 0 19
  • 本文适用的读者 本文写给有一定Promise使用经验的人,如果你还没有使用过Promise,这篇文章可能不适合你,...
    HZ充电大喵阅读 7,288评论 6 19
  • Promise的含义:   Promise是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和...
    呼呼哥阅读 2,158评论 0 16
  • Promise 的含义 一句话概括一下promise的作用:可以将异步操作以同步操作的流程表达出来,避免了层层嵌套...
    雪萌萌萌阅读 5,431评论 0 7