Promise对象用于表示一个异步操作的最终状态(完成或失败)以及其返回的值。---MDN
同步任务会阻塞程序执行(alert、for、...)。
异步任务不会阻塞程序执行(setTimeout、fs.readFile、....)。
promise对象放的是我们未来要做的事情,当我们实例化这个对象后,就开始做这个事。成功做成功的事,失败做失败的事。之前解决异步主要方式是通过回调函数,事件。
使用Promise(then、catch、finally)
Promise.all & Promise.race
Promise.resolve & Promise.reject
// 比较传统的回调方式与promise // ------------------------------------------------------- // 回调 // 方法 用于请求数据(模拟) // function f(cb) { // setTimeout(function() { // cb && cb();//短路操作,与,前面的对了,执行后面的操作。 // }, 1000); // } // f(function() { // console.log(1); // f(function() { // console.log(2); // f(function() { // console.log(3); // f(function() { // console.log(4); // f(function() { // console.log(5); // f(function() { // console.log(6); // }); // }); // }); // }); // }); // }); // ------------------------------------------------------- // promise // 方法 用于请求数据(模拟) function f() { return new Promise(resolve => { setTimeout(function() { resolve(); }, 1000); }) } f() .then(function() { console.log(1); return f();//注意这里返回的是新的promise对象,而不是第一个生成的。 }) .then(function() { console.log(2); return f(); }) .then(function() { console.log(4); return f(); }) .then(function() { console.log(3); return f(); }) .then(function() { console.log(5); return f(); }) .then(function() { console.log(6); });
注意:Promise 新建后立即执行,所以首先输出的是Promise,然后,then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行。
// 信任问题 // 第三方的某个库 function method(cb) { // 未按所想的预期执行回调 setTimeout(function() { // 讲道理应该是现在该调用回调了 cb && cb(); // 但是?? 好像这个库有bug啊 emm 被多调用了一次 cb && cb(); }, 1000); } // promise一但被确定为成功或者失败 就不能再被更改 function method() { return new Promise(resolve => { setTimeout(function() { // 成功 resolve(); resolve(); }, 1000); }); } // 控制反转 function method(cb) { // 未按所想的预期执行回调 setTimeout(function() { // 执行回调 但是添油加醋 cb && cb.call({a: 1, b: 2}); }, 1000); } function method(cb) { return new Promise(resolve => { setTimeout(() => { resolve(); // 调用的resolve全为自己所写书写的流程 很大程度上改善了反转控制的问题 }, 1000); }); }
错误处理:
function f(val) { return new Promise((resolve, reject) => { if (val) { resolve({ name: '小明' });//只能传递一个参数,第二个取不到。 } else { reject('404'); } }); } // then(resolve, reject) // then方法中的第二个回调 失败时候做的事 // f(false) // .then((data) => { // console.log(data) // }, e => { // console.log(e); // }) //---------------------------------------- // catch // 使用实例的catch方法 可以捕获错误 // f(true) // .then(data => { // console.log(data); // return f(false); // }) // .then(() => {//若是错误没有被处理,则中间的代码都不会执行,除非有对错误进行处理。 // console.log('我永远不会被输出'); // }) // .then(() => { // }) // .catch(e => { // console.log(e); // return f(false) ; // }); //---------------------------------------- // finally // 不论成功还是失败 finally中的内容一定会执行 f(true) .then(data => { console.log(data); return f(false); }) .catch(e => { console.log(e); return f(false); }) .finally(() => { console.log(100); });
注意:Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject,它们是两个函数then方法可以接受两个回调函数作为参数,第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用,其中,第二个函数是可选的,不一定要提供,Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数。
Pormise有三种状态:pending进行中,fulfilled成功,rejected失败。状态的改变只有两种,
从pending--》fulfilled,从pending--》rejected。状态的改变不可逆,一旦决议不能再修改。
(1)对象的状态不受外界影响,Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败),只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态,这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果,Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected,只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型),如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果,这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
// Promise.all方法可以把多个promise实例 包装成一个新的promise实例 // Promise.all([ promise1, promise2 ]) : Promise//接收一个数组,数组中的值为promise对象 //若是存在失败的promise,则判断为失败,全部成功才为成功。
//默认会将参数用promise.resolve()包一下。
// 模拟需要多个请求的数据 才能进行下一步操作的情况 // function getData1() { // return new Promise((resolve, reject) => { // setTimeout(() => { // console.log('第一条数据加载成功'); // resolve('data1'); // }, 1000); // }); // } // function getData2() { // return new Promise((resolve, reject) => { // setTimeout(() => { // console.log('第二条数据加载成功'); // resolve('data2'); // }, 1000); // }); // } // function getData3() { // return new Promise((resolve, reject) => { // setTimeout(() => { // console.log('第三条数据加载成功'); // resolve('data3'); // }, 1000); // }); // } // function getData4() { // return new Promise((resolve, reject) => { // setTimeout(() => { // // console.log('第四条数据加载成功'); // reject('data4 err'); // }, 500); // }); // } // let p = Promise.all([]);//传入空数组,决议会成功 // p.then(() => { // console.log('dfsafd'); // }, e => { // console.log(e); // }); // 不是用Promise.all // let count = 0; // let err = false; // function func() { // if (count < 4) return; // if (err) { // // .... // } // console.log('全部拿到了 !'); // } // function getData1() { // setTimeout(() => { // console.log('第一条数据加载成功'); // count ++; // func(); // }, 1000); // } // function getData2() { // setTimeout(() => { // console.log('第二条数据加载成功'); // count ++; // func(); // }, 1000); // } // function getData3() { // setTimeout(() => { // console.log('第三条数据加载成功'); // count ++; // func(); // }, 1000); // } // function getData4() { // setTimeout(() => { // console.log('第四条数据加载成功'); // count ++; // func(); // }, 1000); // } // getData1(); // getData2(); // getData3(); // getData4();
promise.race();//和all不同的地方在于,一旦有一个promise实例成功或者失败了,那该方法马上返回promise实例就为成功或者失败。传入空数组时,什么反应都没有。其他与promise.all()方法相同。
// Promise.resolve() 和 Promise.reject() // 常用来生成已经被决议为失败或者成功的promise实例 // Promise.resolve // --------------------------------------- // 传递一个普通的值 // let p1 = new Promise(resolve => { // resolve('成功!'); // }); // let p2 = Promise.resolve('成功!'); //以上两种方法等价。返回的是promise实例对象。 // // --------------------------------------- // // 传递一个promise实例 // let poruomiesi = new Promise(resolve => { // resolve('耶!') // }); // // 直接返回传递进去的promise // let p = Promise.resolve(poruomiesi); // p.then(data => void console.log(data)); // console.log(p === poruomiesi); //true // // --------------------------------------- // // 传递一个thenable // // 如果传递的是个thenable,则会把他包装成promise对象并立即执行该对象的then方法。//概念和类数组和数组的关系有点类似,鸭子类型。 // let obj = { // then(cb) { // console.log('我被执行了'); // cb('哼!'); // }, // oth() { // console.log('我被抛弃了'); // } // } // // 立即执行then方法 // Promise.resolve(obj).then(data => { // console.log(data); // }); // // Promise.reject // Promise.reject({ then() { console.log(1) } }) // .then(() => { // console.log('我不会被执行'); // }, e => { // console.log(e); // }); // console.log(1); // let p = new Promise(resolve => { // console.log(2); // resolve(); // console.log(3); // }); // console.log(4); // p.then(() => { // console.log(5); // }); // console.log(6); // 把同步的任务转成异步任务 function createAsyncTask(syncTask) {//自己封装的一个将同步任务转化成异步任务的函数 return Promise.resolve(syncTask).then(syncTask => syncTask()); } createAsyncTask(() => { console.log('我变成了异步任务!!!'); return 1 + 1; }).then(res => { console.log(res); }); console.log('我是同步任务!');
小案例:
// 页面中有个板块 需要多张图片加载完之后才能进行展示 const loadImg = src => { return new Promise((resolve, reject) => { const img = new Image(); img.src = src; img.onload = void resolve(img); img.onerror = void reject('加载失败'); }); }; const imgs = [ 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1526734981&di=fe12efe9e3a76bd3bb5ac202a3c76823&imgtype=jpg&er=1&src=http%3A%2F%2Fd15.lxyes.com%2F15xm%2Fact%2F20151105%2F20%2F99112408.jpg', 'https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=1252816855,3131381110&fm=27&gp=0.jpg', 'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1906477750,651116720&fm=27&gp=0.jpg' ]; Promise.all(imgs.map(src => loadImg(src))).then(arr => { console.log(arr); });