想要了解promise就要先了解为什么要用promise以及他解决了什么问题
同步异步就不讲了 从之前解决异步的方法讲起即(回调地狱)
前人如何解决异步问题
from红宝书 这应该是比较权威的讲解了
这个是一个书中的案例 拿来说最不错了 为了能让之后的程序精准的在所需要值计算出来之后立即执行就把他放在这个函数的回调函数中去
这样你等我 我等你 一层又一层 即形成了所熟知的回调地狱
之前处理错误的方式 写好异步回调函数 根据try catch捕捉,异步操作返回值存在时间太短 ,successCallback必须在很短时间内初始化并接收参数 ,不然得不到结果 。所以这种方式已经不可取了。
同时由于回调地狱的存在 期约函数应运而生
promise
Promises/A+规范
2012年Promises/A+组织分叉(fork)了CommonJS的Promises/A建议,制定了Promises/A+规范。这个规范最终成为了ECMAScript 6规范实现的范本。
期约基础
promise通过new操作符来实例化,创建期约函数时需要传入执行器(exexutor)函数作为参数
let p = new Promise(() => { });
setTimeout(console.log, 0, p);
// Promise <pending>
使用 了一个空函数对象来应付一下解释器:如果不提供执行器函数,就会抛出 SyntaxError 。(为了举例子)
运行可得期约有如下三种状态:
待定(pending)
兑现(fulfilled,有时候也成为“解决”,resolved)
拒绝(rejected)
promise只要从待定转换为兑现或拒绝,期约的状态就不再改变。而 且,也不能保证期约必然会脱离待定状态。因此,组织合理的代码无论期约解决 (resolve)还是拒绝(reject),**甚至永远处于待定(pending)状态,**都应该具有恰当 的行为。
期约的状态是私有的,不能直接通过JavaScript检测到。另外,期约的状态也不能被外部 JavaScript代码修改。期约故意将异步行为封装 起来,从而隔离外部的同步代码。
期约主要有两大用途。
首先是抽象地表示一个异步操作。
“待定”表示尚未开始或者正在执行中。“兑现”表示已经成功完成,而“拒 绝”则表示没有成功完成。
期约封装的异步操作会实际生成某个值,而程序期待期约状态改 变时可以访问这个值。
为了支持这两种用例,每个期约只要状态切换为兑现,就会有一个私有的内部值 (value)。类似地,每个期约只要状态切换为拒绝,就会有一个私有的内部理由 (reason)。无论是值还是理由,都是包含原始值或对象的不可修改的引用。二者都 是可选的,而且默认值为 undefined 。在期约到达某个落定状态时执行的异步代码始终会收到这个值或理由。
通过执行函数控制期约状态
执行器函数主要有两项职责:初始化期约的异步行为和控制状态的最终转 换。
通过调用 resolve() 和 reject()控制期约状态的转换
添加 setTimeout 可以推迟切换状态:
let p = new Promise((resolve, reject) => setTimeout(resolve, 1000));
setTimeout(console.log, 0, p);
重申:无论 resolve() 和 reject() 中的哪个被调用,状态转换都不可撤销了。于是继 续修改状态会静默失败,如下所示:
let p = new Promise((resolve, reject) => { resolve(); reject(); });
setTimeout(console.log, 0, p); // Promise <resolved>
为避免期约卡在待定状态,可以添加一个定时退出功能。比如,可以通过 setTimeout 设置一个10秒钟后无论如何都会拒绝期约的回调:
let p = new Promise((resolve, reject) => { setTimeout(reject, 10000); });
setTimeout(console.log, 0, p);
setTimeout(console.log, 11000, p);
Promise.resolve()
期约并非一开始就必须处于待定状态,然后通过执行器函数才能转换为落定状态。通 过调用 Promise.resolve() 静态方法,可以实例化一个成功的期约。
let p1 = new Promise((resolve, reject) => resolve());
let p2 = Promise.resolve();
console.log(p1); //fulfilled
console.log(p2); //fulfilled
这个解决的期约的值对应着传给 Promise.resolve() 的第一个参数。使用这个静态方法,实际上可以把任何值都转换为一个期约:
如果这个函数转换一个已经是期约的东西 则仍是期约并且会保留传入期约的状态
let p = new Promise(() => { });
setTimeout(console.log, 0, p); // Promise <pending>
setTimeout(console.log, 0, Promise.resolve(p)); // Promise <pending>
setTimeout(console.log, 0, Promise.resolve(Promise.resolve(3))); //
setTimeout(console.log, 0, p === Promise.resolve(p)); // true
let p = Promise.resolve(new Error('foo'));
setTimeout(console.log, 0, p);
如果放入一个失败的期约则仍然显示成功但会导致不符合预期的行为
let p = Promise.resolve(new Error('foo'));
setTimeout(console.log, 0, p);
Promise.reject()
其余与resolve相同但是当放入一个期约对象时 这个对象会变成它返回的拒绝期约的理由
同步异步执行的二元性
try {
throw new Error('foo');
} catch(e) {
console.log(e); // Error: foo
}
try {
Promise.reject(new Error('bar'));
} catch(e) {
console.log(e);
}
代码一旦开始以异步模式执行,则唯一与之交互的方式就是使用异步结构(promise)
期约的实例方法
连接外部同步代码与内部异步代码之间的桥梁
Promise.prototype.then()
then方法只能接收成功和失败两个处理函数
function onResolved(id) { setTimeout(console.log, 0, id, 'resolved'); }
function onRejected(id) { setTimeout(console.log, 0, id, 'rejected'); }
let p1 = new Promise((resolve, reject) => setTimeout(resolve, 3000));
let p2 = new Promise((resolve, reject) => setTimeout(reject, 3000));
p1.then(() => onResolved('p1'), () => onRejected('p1'));
p2.then(() => onResolved('p2'), () => onRejected('p2'));
另外
如果给then非函数类型的参数会被默认忽略
如果只想提供失败参数 那么就要在成功的位置上传入undefined
function onResolved(id) { setTimeout(console.log, 0, id, 'resolved'); }
function onRejected(id) { setTimeout(console.log, 0, id, 'rejected'); }
let p1 = new Promise((resolve, reject) => setTimeout(resolve, 3000));
let p2 = new Promise((resolve, reject) => setTimeout(reject, 3000));
p1.then('gobbeltygook');
p2.then(null, () => onRejected('p2'));
Promise.prototype.then()会返回一个新的期约实例
这个期约实例是由成功处理程序的返回值所构建(用promise.resolve包装来生成新的期约)
若没有处理程序则会包装上一个期约解决之后的值
如果没有显式的返回语句,则会包装默认的undefined
没有处理程序的情况
let p1 = Promise.resolve('foo'); // 若调用then()时不传处理程序,则原样向后传
let p2 = p1.then();
setTimeout(console.log, 0, p2);
// Promise <resolved>: foo
若没有返回值时
// 这些都一样
let p3 = p1.then(() => undefined);
let p4 = p1.then(() => { });
let p5 = p1.then(() => Promise.resolve());
setTimeout(console.log, 0, p3); // Promise <resolved>: undefined
setTimeout(console.log, 0, p4); // Promise <resolved>: undefined
setTimeout(console.log, 0, p5); // Promise <resolved>: undefined
如果有显示的返回值则会用Promise.resolve来包装这个值
如果有返回值 则会用resolve正常包装这个
// 这些都一样
let p6 = p1.then(() => 'bar');
let p7 = p1.then(() => Promise.resolve('bar'));
setTimeout(console.log, 0, p6); // Promise <resolved>: bar
setTimeout(console.log, 0, p7); // Promise <resolved>: bar
// Promise.resolve()保留返回的期约
let p8 = p1.then(() => new Promise(() => { }));
let p9 = p1.then(() => Promise.reject());
// Uncaught (in promise): undefined
setTimeout(console.log, 0, p8); // Promise <pending>
setTimeout(console.log, 0, p9); // Promise <rejected>: undefined