es6 promise承诺

promise

是异步的一种解决方案 相当于优化es5的回调函数和事件 是一个构造函数
promise是一个对象 可以通过Object.getOwnPropertyNames() 来查看promise本身和prototype上的api方法

//["length", "name", "prototype", "all", "race", "resolve", "reject", "finally", "try"]
//["constructor", "then", "catch", "finally"]

特点:
1.对象的状态不受外界影响 promise对象代表一个异步操作的时候会有3种状态: pedding(进行中) fulfilled(已成功) rejected(已失败) 只有异步操作的结果 才可以决定当前是哪一种状态 其他操作都不可以
2.一旦状态发生改变,就不会再变 所以状态的变更就只有2种可能 pedding 到fulfilled 或者pedding 到 rejected 当状态变更之后状态就会凝固 不会再变 会一直保持这个结果 称之为resolved(已定型)
promise 避免了es5在请求异步的时候 层层嵌套回调函数的坑爹行为 并且同意接口 控制异步操作更加容易
缺点:1.无法取消promise 一旦新建就会立即执行 无法中断 2.如果不设置回调函数 promise内部抛出的错误 不会反应到外部 3.当处于pedding的状态时 无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)
remark:如果某些事件不断地反复发生 一般来说 使用stream模式是比部署Promise 更好的选择

基本写法

const promise = new Promise((resolve,reject) =>{
    console.log('Promise');
    resolve('resolved');
})
promise.then(value =>{
    console.log(value);
},value => {
  console.log('error',value)
})
//或者(建议第二种)
promise.then(value =>{
    console.log(value)
}).catch(value => {
    console.log('error',value)
})
console.log('Hi!')
//Promise
//Hi!
//resolved

resolve 的作用是将promise对象的状态有pedding变为resolve,在异步操作成功时调用 执行promise.prototype.then第一个函数方法
reject 的作用是将promise对象的状态从pedding变为rejected 在异步操作报出错误时调用 带着参数 执行promise.prototype.then第二个函数方法 或者catch函数(建议catch函数)

举例中 Promise 新建后立即执行,所以首先输出是Promise 然后then方法执行的回调函数,将在当前脚本所有同步任务执行完才会执行,所以resolved最后输出

建立一个promise对象实现ajax请求的例子

const getJSON = function(url){
  const promise = new Promise(function(resolve,reject){
    const handler = function(){
        if(this.readyState !== 4){
            return;
        }
        if(this.status == 200){
            resolve(this.response)
        }else{
            reject(new Error(this.statusText));
        }
    };
    const client = new XMLHttpRequest();
    client.open('GET',url);
    client.onreadystatechange = hander;
    client.responseType = 'json';
    client.setRequestHeader('Accept','application/json');
    client.send();
  });
  return promise;
}
getJSON('/posts.json').then(json => {
   console.log('Contents:' + json);
}).catch(error => {
   console.error('出错了',error)
});
const p1 = new Promise(function(resolve,reject){
    setTimeout(() => reject(new Error('fail')),3000)
    console.log(1)
})
const p2 = new Promise((resolve,reject) => {
    setTimeout(() => resolve(p1),1000)
 
})
p2.then(result => console.log(result))
.catch(error => console.log(error))

当resolve函数的参数为另外一个promise实例的时候 p1的状态会传递给p2 也就是说p1的状态决定了p2的状态 如果p1的状态是pedding 那么p2的回调函数就会等待p1的状态改变 如果p1的状态已经是resolved或者rejected 那么p2的回调函数将会立即执行
上面代码中 p1为promise 3秒变为rejected p2的状态在1秒之后改变 resolve方法返回是p1 由于p2返回的是另一个Promise 导致p2自己的状态无效了 由于p1的状态决定p2的状态 所以后面的then语句都变成针对后者(p1) 又过了2秒 p1变为rejected 导致触发catch方法指定的回调函数

当p2的1000毫秒 改为5000毫秒的时候 会出现报错 感觉应该是p2还并未执行开始执行resolve方法函数的原因 会有2秒的报错时间 在p2开始运行resolve方法函数之后 调用p2的catch方法函数

另外注意:调用resolve和reject并不会终结Promise的参数函数的执行 也就是说 如果Promise实例还存在同步执行的其他代码 依然还是会执行 不会掉出promise实例 不过一般来说 后面执行的代码 应该放在then方法里面执行更好一点

Promise.prototype.then()

Promise实例具有then方法 也就是说 then方法是定义在prototype上的 作用是为Promise 实例添加状态改变时的回调函数。
then第一个参数是resolve状态 第二个为rejected状态(建议使用catch)
then 返回的方法是一个新的Promise实例(注意不是原来那个Promise实例) 所以可以采用链式写法 then方法后面可以再调用另外一个then方法

Promise.prototype.catch()

处理发生错误时的回调函数
Promise 对象抛出的错误不会传递到外层代码 即不会有任何反应 浏览器的调试工具会报错 但是不会影响代码的运行
通俗的讲就是 Promise会吃掉错误

const promise = new Promise(function (resolve, reject) {
  resolve('ok');
  setTimeout(function () { throw new Error('test') }, 0)
});
promise.then(function (value) { console.log(value) });
// ok
// Uncaught Error: test

上面代码中 Promise指定在下一轮'事件循环'再抛出错误 到了那个时候Promise的运行已经结束了 所以这个错误是在Promise函数体外抛出的 会冒泡到最外层,成了未捕获的错误
一般情况下 总是建议 Promise对象后面要跟一个catch方法 来捕获Promise内部发生的错误 而且因为返回的是一个新的promise实例 所以还可以接着调用then方法
当然 如果没有错误 还是跟之前一样 会跳过catch方法 但是如果跟在catch后面的then方法里面出现报错 就与前面的catch无关了

Promise.prototype.finally()

指不管Promise对象最后状态如何 都会执行的操作 不依赖于Promise的执行结果 该方法是es2018(ES9)引入标准的
finally 本质上是then方法的特例

promise
.finally(() => {
  // 语句
});

// 等同于
promise
.then(
  result => {
    // 语句
    return result;
  },
  error => {
    // 语句
    throw error;
  }
);
Promise.prototype.finally = function (callback) {
  let P = this.constructor;
  return this.then(
    value  => P.resolve(callback()).then(() => value),
    reason => P.resolve(callback()).then(() => { throw reason })
  );
};
Promise.all()
const p = Promise.all([p1, p2, p3]);
// 生成一个Promise对象的数组
const promises = [2, 3, 5, 7, 11, 13].map(function (id) {
  return getJSON('/post/' + id + ".json");
});

Promise.all(promises).then(function (posts) {
  // ...
}).catch(function(reason){
  // ...
});
const databasePromise = connectDatabase();

const booksPromise = databasePromise
  .then(findAllBooks);

const userPromise = databasePromise
  .then(getCurrentUser);

Promise.all([
  booksPromise,
  userPromise
])
.then(([books, user]) => pickTopRecommentations(books, user));

用于将多个Promise实例包装成一个新的Promise实例
方法接受一个数组作为参数 如果不是就会先调用Promise.resolve方法 将参数转化为Promise实例再进一步处理(Promise.all方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。)

p的状态由p1、p2、p3决定
1 只有3个都变成fulfilled p的状态才会变成fulfilled 此时3者的返回值组成一个数组 传递给p的回调函数
2 只要其中一个为rejected p的状态就变成rejected 此时第一个被rejected的实例的返回值,会传递给p的回调函数

如果作为参数的Promise实例 自己定义了catch方法 那么它一旦被rejected 并不会出发Promise.all()的catch方法
因为Promise 实例中的catch方法 返回一个新的Promise实例 这个Promise实例会调用resolve()方法

如果实例本身没有catch方法 就会调用promise.all()的catch方法

Promise.race() (比赛)
const p = Promise.race([p1, p2, p3]);

只要数组其中一个率先改变状态 p的状态就跟着改变
其他的跟Promise.all()一致

const p = Promise.race([
  fetch('/resource-that-may-take-a-while'),
  new Promise(function (resolve, reject) {
    setTimeout(() => reject(new Error('request timeout')), 5000)
  })
]);

p
.then(console.log)
.catch(console.error);

//fetch 新的请求接口的api  返回一个Promise对象
Promise.resolve()

将现有对象转化为Promise对象

const jsPromise = Promise.resolve($.ajax('/whatever.json'));
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
  1. 如果参数是Promise 实例 那么Promise.resolve 将不做任何修改、原封不动的返回
    2.如果是thenable对象(指具有then方法的对象)
let thenable = {
  then: function(resolve, reject) {
    resolve(42);
  }
};

let p1 = Promise.resolve(thenable);
p1.then(function(value) {
  console.log(value);  // 42
});

Promise.resolve 方法会将这个对象转为Promise对象,然后就立即执行thenable对象的then方法

3.参数不具有then方法的对象或根本不是对象
原始值或者不具有then方法的对象 Promise.resolve方法返回一个新的Promise对象,状态为resolved

4.不带有任何参数
直接返回一个resolved状态的Promise对象
所以如果希望得到一个Promise对象,比较方便的方法就是直接调用Promise.resovel方法

setTimeout(function () {
  console.log('three');
}, 0);

Promise.resolve().then(function () {
  console.log('two');
});

console.log('one');

// one
// two
// three

需要注意的是 立即resolve的Promise对象 实在本轮‘事件循环’的结束时,而不是在下一轮‘事件循环’的开始时

Promise.reject()

该方法也会返回一个新的Promise实例,该实例的状态为rejected

const thenable = {
  then(resolve, reject) {
    reject('出错了');
  }
};

const p = Promise.reject(thenable);
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))

p.catch(e => {
  console.log(e === thenable);
  console.log(e)
})
//true
捕获错误的参数是原封不动的 为thenable对象 这一点与resolve不一致 

const thenable = {
  then(resolve, reject) {
    resolve('出错了');
  }
};

const p = Promise.resolve(thenable);


p.then(e => {
  console.log(e === thenable);
  console.log(e)
})
//false
//出错了
加载图片
const preloadImage = function(path){
  return new Promise((resolve,reject) => {
    const image = new Image();
    image.onload = resolve;
    image.onerror = reject;
    image.src = path;
  })
}
Generator与Promise结合 返回Promise对象
function getFoo () {
  return new Promise(function (resolve, reject){
    resolve('foo');
  });
}

const g = function* () {
  try {
    const foo = yield getFoo();
    console.log(foo);
  } catch (e) {
    console.log(e);
  }
};

function run (generator) {
  const it = generator();

  function go(result) {
    if (result.done) return result.value;

    return result.value.then(function (value) {
      return go(it.next(value));
    }, function (error) {
      return go(it.throw(error));
    });
  }

  go(it.next());
}

run(g);
Promise.try()
const f = () => console.log('now');
Promise.resolve().then(f);
console.log('next');
// next
// now

在不知道或者不想区分 函数f是同步函数还是异步操作,但是想用Promise来处理它。因为这样就可以不管f是否包含异步操作,都用then方法执行下一步流程,用catch方法处理f抛出的错误。一般就会采用上面的写法。
但是建立空的Promise.resolve对象的执行 因为是同步函数 那么它会在本轮事件循环的末尾执行。

上面代码中 函数f是同步的 但是用Promise包装之后 变成异步执行了

需要找到一种方法 让同步函数同步执行 异步函数异步执行 并且让他们具有统一的api
第一种用async()函数

async函数
const f = () => console.log('now');
(async () => f())();
console.log('next');
// now
// next

第二种使用new Promise()

const f = () => console.log('now');
(
  () => new Promise(
    resolve => resolve(f())
  )
)()
console.log('next')
//now
//next

所以有了Promise.try()的提案

Promise.try(database.users.get({id: userId}))
  .then(...)
  .catch(...)

事实上Promise.try就是模拟try代码块 就像Promise.catch模拟的是catch代码块

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

推荐阅读更多精彩内容

  • Promise 对象 Promise 的含义 Promise 是异步编程的一种解决方案,比传统的解决方案——回调函...
    neromous阅读 8,556评论 1 56
  • Promise含义 Promise是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更强大。所谓Pr...
    oWSQo阅读 1,078评论 0 4
  • Promiese 简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果,语法上说,Pr...
    雨飞飞雨阅读 3,329评论 0 19
  • 一、Promise的含义 Promise在JavaScript语言中早有实现,ES6将其写进了语言标准,统一了用法...
    Alex灌汤猫阅读 797评论 0 2
  • 正确地爱一个人,就是要给她成熟的爱。 成熟的爱,包含这样四个原则,如何正确地爱一个人,要做到这四点。 让人舒服,对...
    智慧之窗阅读 1,279评论 0 1