ECMAScript6基础学习教程(八)Promise

JavaScript被设计为单线程(webWoker可以处理多线程),利用事件轮询机制,可以模拟出多线程效果,也就是异步操作,而回调函数callback是事件轮询调用的目标方法。

但是,通过回调函数处理异步事件有很多不确定性,并且容易陷入“回调地狱”-嵌套太深。于是,Promise概念被提出,并且很多JavaScript框架(比如JQuery)支持的异步API都基于Promise理念构建的。

1.什么是Promise?

Promise是一个对象,用来传递异步操作的信息,它代表了某个未来时刻才知道结果的事件,并且这个事件提供统一API接口。

2. Promise对象

Promise原型对象提供的主要方法有:

//添加状态改变时的回调函数
Promise.prototype.then(resolvedFunc, rejectedFunc) 
//响应rejected状态的promise(如果前面有错误抛出,会产生一个rejected状态的promise)
//相当于promise.then(null, rejectedFunc)
Promise.prototype.catch(rejectedFunc) 

参考一个标准的Promise实例:

var promise = new Promise(function(resolve, reject){
  // 你的代码---pending状态
  if (isSuccessful) {
    resolve(value); // 成功
  }
  else {
    reject(error); // 失败
  }
// 这里的代码永远不会被运行
});

promise
.then(
  function(value){//resolved时调用}, 
  function(error){// rejected时调用})
.catch(function(error){//有错误抛出时调用});

promise对象有如下特点:

  1. 可以利用promise对象创建一个异步操作。
  2. 有三种状态:pending, resolved和rejected。异步代码运行时为pending,运行后的结果只会是两种:成功-resolved,或者失败-rejected。状态变化是单行流动,不可逆转。
  3. 在一个promise里,resolve或者reject方法只会被调用一次。
  4. resolve()/reject()可以利用参数传递数据,但是,只支持传递第一个参数。也就是说,promise决议只能传递单个值/对象。因此,实际应用中,需要将多个值封装在一个对象中传递
  5. then()和catch()函数都会默认返回一个promise对象。
  6. 如果没有给then()传递函数作为完成处理函数参数,还是会有替代的默认处理函数,并且,该默认函数会把接受到的值传递给下一个promise对象。
getPromise(40, true).
    then(null,null).then(function (value) {
    console.log("resolved:"+value);
});
// 打印:resolved:40
// 可见,如果不设置then的处理函数参数,resolved值40一直会被传递下去。

3. 一个Promise实例

下面是一个Promise例子,参考注释。
创建promise的工厂方法:

var getPromise = function (val, isSuccessful) {
    var promise = new Promise(function (resolve, reject) {
        setTimeout(function () {
            if (isSuccessful) {
                // 决议成功,回调then()的第一个函数参数
                resolve(val); 
            }
            else {
                // 决议失败,回调then()的第二个函数参数
                reject(new Error("oh, error!")); 
            }
        }, 0);
    });
    return promise;
};

第一次测试(连续调用两次promise):

getPromise(30, true).then((value) => {
    console.log("the first resolved status: " + value);
    // 返回一个promise对象,该promise决议结果会决定下一个then()函数应该调用哪个回调函数
    // 如果不显性返回promise对象,ES6会默认创建一个空值promise对象最为返回值
    return getPromise(20, true);
}, (error) => {
    console.log("the second rejected status: " + error);
}).then((value) => {
    console.log("the second resolved status: " + value);
    // 一个rejected状态的promise对象被返回
    //由于后续没有then(),因此catch函数捕获错误状态
    throw new Error('create a error!');
}, (error) => {
    console.log("the second rejected status: " + error);
}).catch((error) => {
    console.log("Catch: " + error);
});

//运行结果:
the first resolved status: 30
the second resolved status: 20
Catch: Error: create a error!

第二次测试(多加一个then调用):

getPromise(30, true).then((value) => {
    console.log("the first resolved status: " + value);
    return getPromise(20, true);
}, (error) => {
    console.log("the second rejected status: " + error);
}).then((value) => {
    console.log("the second resolved status: " + value);
    // 一个rejected状态的promise对象被返回
    //由于后续有then(),因此then函数的第二个回调函数被运行
    throw new Error('create a error!');
}, (error) => {
    console.log("the second rejected status: " + error);
}).then((value) => {
    console.log("the third resolved status: " + value);
}, (error) => {
    console.log("the third rejected status: " + error);
}).catch((error) => {
    console.log("Catch: " + error);
});

// 运行结果:
the first resolved status: 30
the second resolved status: 20
the third rejected status: Error: create the first error!

第三次测试(修改getPromise函数):

var getPromise = function (val, isSuccessful) {
    var promise = new Promise(function (resolve, reject) {
         if (isSuccessful) {
                resolve(val);
                //resolve之后抛出错误,是不会被捕获的
                throw new Error('error, error, error!');
            }
            else {
                reject(new Error("oh, error!"));
            }
    });
    return promise;
};
// 测试1和测试2的运行结果不会改变

从上面的例子可以看到,可以用同步书写方式连续调用多个异步请求。并且,Promise还提供了静态函数帮助解决更复杂的异步编程场景。

4. Promise静态方法

Promise提供的静态方法有:

Promise.all([promise1, promise2 [,promiseN]])
Promise.race([promise1, promise2 [,promiseN])
Promise.resolve()
Promise.reject()
(1) Promise.all()

将多个promise实例包装成一个新的promise对象,只有多个promise的状态都为resolved,Promise.all()的状态才会变为resolved。
继续上面的例子,测试如下:

Promise.all([
    getPromise(20, true),
    getPromise(30, true), 
    getPromise(40, true)])
    .then(function (value) {
        console.log(value);
    });
// 输出为:[ 20, 30, 40 ]

每个Promise实例的resolved值都会暂存在一个数组里,最后,该数组被传递到Promise.all()的resolved回调函数。

(2) Promise.race()

将多个promise实例包装成一个新的promise对象,只有第一个promise状态为resolved时,Promise.race()的状态才变为resolved

并且,第一个promise的值会传递给Promise.race()。

基于上面例子继续测试:

Promise.race([
    getPromise(20, true),
    getPromise(30, false),
    getPromise(40, false)])
    .then(function (value) {
        console.log(value);
    }, function (error) {
        console.log(error);
    });
//输出为: 20

Promise.race([
    getPromise(20, false),
    getPromise(30, true),
    getPromise(40, false)])
    .then(function (value) {
        console.log(value);
    }, function (error) {
        console.log(error);
    });
// 输出为: [Error: oh, error!]
(3) Promise.resolve()

该方法会返回一个Promise对象,情况分为两种:

  • 如果目标对象不是Promise对象,该方法会创建一个Promise对象
  • 如果目标对象本身就是Promise对象,该方法会将这个Promise对象直接返回
// p2和p1行为完全一样
var p1 = new Promise(function(resolve, reject){
  resolve(40);
});
var p2 = Promise.resolve(40);
// 向Promise.resolve()传递一个Promise对象,则直接返回这个对象
var p3 = Promise.resolve(p2);
console.log(p2===p3); // true
(4) Promise.reject()

返回一个新的Promise实例,状态为rejected:

var p = new Promise(function(resolve, reject){
  reject("error");
}); 

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

推荐阅读更多精彩内容