第11章 Promises

Promise

promises是为了解决异步编程中回调函数存在的一些问题而产生的,它不是用了取代回调函数的,而是使得回调函数更加的好用。Node.js使得异步编程更加的流行,promise的用处也得到了很好的体现。

  1. 基本语法;
  2. Promise状态和Promise静态方法 Promise.resolve(), Promise.reject();
  3. 异常处理,通过事件的方式;
  4. 另外2个静态方法: Promise.all(), Promise.race()

一.基本语法

1.异步编程的几种模式

我们知道javascript是单线程的,所有要执行的代码都放在队列中,称之为job queue.
一般js异步模式可以分为: callback模式, promise模式, generator模式

2.什么是promise

promise可以称为future,是一个异步操作的占位符,首先它是一个对象。它有2种状态状态:

  1. unsettled状态: 表示异步操作还未完成,内部属性[[PromiseState]]设置为 pending;
  2. settled状态: 表示异步操作完成,成功: 内部属性[[PromiseState]]设置为 fulfilled; 失败rejected.

3.then()方法

当promise完成操作之后,可以通过 then()方法进行操作,then语法:

then(fulfillmentCallback, rejectCallback)

第一个回调表示成功之后执行,第二个回调表示失败后执行,这两个回调只会执行一个,也可以分开写:

then(fulfillmentCallback);

then(null, rejectCallback);
// 等价于
catch(rejectCallback)

4.thenable对象

一个对象拥有一个then()方法,我们称这个对象为 thenable对象all promises are thenable, but all thenables are not promises

// 返回一个对象,包含一个then方法
function foo(x) {
    return {
        then: function(resolve, reject) {
            setTimeout(resolve, 0, "回掉函数"+x);
            return this;
        }
    };
}
Promise.all([foo(1), foo(2)]).then(function(args) {
    console.log(args);
})

// ["回调参数1", "回调参数2"]

二.创建一个promise

1.创建一个 Unsettled Promises, 使用构造器

新的promises通过Promise构造函数创建,此时promise状态为pending, 这个构造函数接受唯一一个参数: 一个function,我们称之为 executor (这个executor不是异步的,调用时会立即执行).

而这个executor包含2个参数: 2个函数 resolve(), reject()。当executor成功执行,会调用resolve;当executor执行失败,会调用rejected()

下面以Node.js读取文件为例子:

let fs = requrie("fs");

function readFile(filename) {
    return new Promise(function(resolve, reject) {
        fs.readFile(filename, {encoding: "utf8"}, function(err, contents) {
            // Node.js 的 `error-first`模式
            // 失败调用reject
            if (err) {
                reject(err);
                return;
            }
            // 成功时调用resolve
            resolve(contents);
        });
    });
}

let promise = readFile("a.txt");
promise.then(function(contents) {
    console.log(contents);
}).catch(function(err) {
    console.log(err.message);
})

2.创建 Settled promises: Promise.resolve(), Promise.reject()

通过构造器我们可以创建 unsettled promises, 我们可以通过静态方法 Promise.resolve(),Promise.rejected()创建 settled promises.

  • Promise.resolve(): 接受一个参数,返回一个promise,在 fulfilled 状态
  • Promise.reject(): 接受一个参数,返回一个promise, 在 rejected 状态

1.Promise.resolve():

添加一个fulfillment handler来接受返回的promise中的数据

var promiseResolve = Promise.resolve(42);
promiseResolve.then(function(value) {
    console.log(value);      // 42
})

如果reject handler添加给这个promise,则这个handler将永远不会调用,因为一但promise状态确定将不会更改,即:

var promiseResolve = Promise.revolve(42);
// 这个handler永远不会执行
promiseRevolve.catch(function(err) {
    console.log(err);
});

2.Promise.reject():

添加一个 rejected handler:

var promiseRejected = Promise.reject(42);
promiseRejected.catch(function(value) {
    console.log(value);          // 42
});

3.thenable对象可以作为参数传入Promise.resolve静态方法中,依据thenable方法中调用的函数

此条在chrome中存在问题,待更正, 可以在Node.js中运行

如果thenable中调用resolve则返回的promise为fulfilled状态;如果thenable中调用的是reject则返回的promise是rejected状态:

let thenable1 = {
    then: function(resolve, reject) {
        resolve(42);
    }
};

var p1 = Promise.resolve(thenable1); // "fulfilled"状态
p1.then(function(value) {    // 使用fulfillment handler
    console.log(value); // 42
});

let thenable2 = {
    then: function(resolve, reject) {
        reject(43);
    }
};

// 注意这个地方也是使用Promise.resolve()
var p2 = Promise.resolve(thenable2); // "rejected"状态
p2.catch(function(value) {  // 使用reject handler
    console.log(value); // 43
});             

三.全局Promise Rejection 处理

promise最有争议的地方就是当一个promise失败但是没有rejection handler处理错误时静默失败。不过浏览器和Node.js都有相应的处理机制,两者大同小异,都是通过事件的方式监听。

  1. Node.js Rejection Handling

两个事件:

  1. unhandledRejection:当promise失败(rejected),但又没有处理时触发,event handler 有2个参数: reason,promise;
  2. rejectionHandled: 当promise失败(rejected),被处理时触发,hanler 有1个参数: promise;
let rejected;
process.on("unhandledRejection", function(reason, promise) {
    console.log(reason.message); // "boom"
    console.log(rejected === promise); // true
});
rejected = Promise.reject(new Error("boom")); // 出现reject, 没有catch处理

处理:

let rejected;
process.on("rejectionHandled", function(promise) {
    console.log(rejected === promise); // true
});
rejected = Promise.reject(new Error("boom"));

// 等待添加rejection handler
setTimeout(function() {
    rejected.catch(function(value) {    // 处理rejection
        console.log(value.message); // "boom"
    });
}, 1000);

将可能出现的rejection存储在map中,然后检查是否存在相应的handler:

let rejections = new Map();

// 当一个rejection出现,添加到map中
process.on("unhandledRejection", function(reason, promise) {
    rejections.set(promise, reason);
});

// 存在handler,则将其从map集合中删除
process.on("rejectionHanled", function(promise) {
    rejections.delete(promise);
});
// 间隔一段时间对集合中的查看处理,清空
setInterval(function() {
    rejections.forEach(function(reason, promise) {
        console.log(reason.message ? reason.message : reason);
    
        // 用某种方法处理这些rejections
        handleRejection(promise, reason);
    });
    // 清空集合
  rejections.clear();
}, 60000);

2.浏览器rejection处理

浏览器和Node.js处理大同小异,通过事件:

  1. unhandledRejection
  2. rejectionhandled

这2个事件的 event 对象都包含3个属性: type, promise, reason(这点和Node.js不同)

处理方式:

var rejections = new Map();
window.onunhandledrejection = function(event) {
    rejections.set(event.promise, event.reason);
}
window.onrejectionhandled = function(event) {
    rejections.delete(event.promise);
}
setInterval(function() {
    rejections.forEach(function(event) {
        console.log(event.reason.message ? event.reason.message : event.reason);
        
        // 处理
        handleRejection(event.promise, event.reason);
    });
    rejections.clear();
}, 60000);

四.Promise.all() Promise.race()

1.Promise.all()

这个静态方法接受一个可迭代的promises(比如数组),当数组中的每个promise都为fulfilled状态时,返回一个promise.当失败时,立马结束而不等待其他promise完成

let p1 = new Promise((resolve, reject) => {resolve(42)});
let p2 = new Promise((resolve, reject) => {resolve(43)});
let p3 = new Promise((resolve, reject) => {resolve(44)});
let p4 = Promise.all([p1, p2, p3]);
p4.then(function(value) {
    console.log(Array.isArray(value));  // true value为一个数组
    console.log(value[0]); // 42
    console.log(value[1]); // 43
    console.log(value[2]); // 44
});

其中也promise 状态为rejected:

let p1 = new Promise((resolve, reject) => {resolve(42)});
let p2 = new Promise((resolve, reject) => {reject(43)}); // reject
let p3 = new Promise((resolve, reject) => {resolve(44)});
let p4 = Promise.all([p1, p2, p3]);
p4.catch(function(value) {
    console.log(Array.isArray(value)); // false 
    console.log(value);  // 43
})

当有promise被rejected的时候,value永远为单个值,而不是一个数组

2.Promise.race()

返回一个合适的promise,只要数组中任何promise先完成了。如果先fulfilled,则返回的promise为fulfilled状态; 如果一个promise 先 reject,则返回的promise为 rejected状态。

let p1 = new Promise((resolve, reject) => resolve(42));
let p2 = Promise.reject(43); // 直接为rejected状态
let p3 = new Promise((resolve, reject) => resolve(44));
let p4 = Promise.race(function(value) {
    console.log(value); // 43
});

总结

上面讲到的都是一些关于promise的基本概念,而没有深入的研究,比说promise chaining, promise如何处理异步任务等等,均未涉及,这些内容还需要我以后更深入的学习总结,今天到此为止,推荐一篇讲解比较的文章。

promise 详细文档

2016/9/21 17:47:43

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

推荐阅读更多精彩内容

  • 你不知道JS:异步 第三章:Promises 在第二章,我们指出了采用回调来表达异步和管理并发时的两种主要不足:缺...
    purple_force阅读 2,046评论 0 4
  • 你不知道JS:异步 第三章:Promises 接上篇3-1 错误处理(Error Handling) 在异步编程中...
    purple_force阅读 1,384评论 0 2
  • 00、前言Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区...
    夜幕小草阅读 2,126评论 0 12
  • 其实递归我们经常用 但是到底如何定义递归? 到底什么情况下使用递归? 递归能将复杂问题简单化,但是递归的缺点又是什...
    define南拳阅读 360评论 0 0
  • 天刚蒙蒙亮,国梁把被子折叠整理,把昨天的烤红薯和几张粮票放在了窗户上面当做房钱,他轻轻的带上门,准备出发去学校。回...
    吴松乾阅读 430评论 0 0