Promise小结
Promise
由来
假设存在一个名为createAudioFileAsync()的方法,这个方法通过一个参数以及两个回调函数创建一个声音文件,在声音文件成功创建或者创建失败后执行回调函数。
function successCallback(result) {
console.log("Audio file ready at URL: " + result);
}
function failureCallback(error) {
console.log("Error generating audio file: " + error);
}
createAudioFileAsync(audioSettings, successCallback, failureCallback)
新式函数返回一个你可以直接绑定回调函数的promise对象,来代替旧式的函数形式:
const promise = createAudioFileAsync(audioSettings);
promise.then(successCallback, failureCallback);
//更简单点
createAudioFileAsync(audioSettings).then(successCallback, failureCallback);
<strong>我们把这个称为异步函数调用,这种形式有若干优点</strong>
优点: 链式调用
一个常见的需求就是连续执行两个或者多个异步操作,这种情况下,每一个后来的操作都在前面的操作执行成功之后,带着上一步操作所返回的结果开始执行。我们可以通过创造一个promise chain来完成这种需求。
基本上,每一个promise代表了链式中另一个异步过程的完成。
// 经典的回调地狱
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('Got the final result: ' + finalResult);
}, failureCallback);
}, failureCallback);
}, failureCallback);
// 新式函数
doSomething().then(function(result) {
return doSomethingElse(result);
})
.then(function(newResult) {
return doThirdThing(newResult);
})
.then(function(finalResult) {
console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);
// 优化
doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => {
console.log(`Got the final result: ${finalResult}`);
})
.catch(failureCallback);
Catch 的后续链式操作
- 链式中的一个动作失败之后还能有助于新的动作继续完成
new Promise((resolve, reject) => {
console.log('Initial');
resolve();
})
.then(() => {
throw new Error('Something failed');
console.log('Do this');
})
.catch(() => {
console.log('Do that');
})
.then(() => {
console.log('Do this whatever happened before');
});
// Initial
// Do that
// Do this whatever happened before
catch(failureCallback) 是 then(null, failureCallback) 的缩略形式
错误传播
doSomething()
.then(result => doSomethingElse(value))
.then(newResult => doThirdThing(newResult))
.then(finalResult => console.log(`Got the final result: ${finalResult}`))
.catch(failureCallback);
基本上,一个promise链式遇到异常就会停止,查看链式的底端,寻找catch处理程序来代替当前执行。在同步的代码执行之后,这是非常模型化的
try {
let result = syncDoSomething();
let newResult = syncDoSomethingElse(result);
let finalResult = syncDoThirdThing(newResult);
console.log(`Got the final result: ${finalResult}`);
} catch(error) {
failureCallback(error);
}
在ECMAScript 2017标准的async/await语法糖中,这种同步形式代码的整齐性得到了极致的体现
async function foo() {
try {
let result = await doSomething();
let newResult = await doSomethingElse(result);
let finalResult = await doThirdThing(newResult);
console.log(`Got the final result: ${finalResult}`);
} catch(error) {
failureCallback(error);
}
}
在旧式回调API中创建Promise
有一些API仍然使用旧式的被传入的成功或者失败的回调
典型的例子就是setTimeout()函数:
setTimeout(() => saySomething("10 seconds passed"), 10000);
混用旧式回调和promise是会有问题的。如果 saySomething 函数失败了或者包含了编程错误,那就没有办法捕获它了。
幸运的是我们可以用promise来包裹它。最好的做法是将有问题的函数包装在最低级别,并且永远不要再直接调用它们:
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
wait(10000).then(() => saySomething("10 seconds")).catch(failureCallback);
时序 - 异步操作
Promise.resolve(console.log(0)).then(() => console.log(2));
console.log(1);
// 0 1 2
aFun = new Promise ((resolve) => {
console.log('aFun')
resolve()
})
function bFun () {
console.log('bFun')
}
let promise = aFun.then(() => console.log('bFun'))
promise
console.log('promise ---')
// aFun promise bFun
let promise = async function promise () {
await console.log('aFun')
await console.log('bFun')
}
promise()
console.log('promise ---')
// aFun promise bFun
await promise()
console.log('promise----')
// aFun bFun promise
原理分析
Promise中实现 值传递 Chain Promise
function Promise(fn) {
var state = 'pending';
var value;
var deferred = null;
function resolve(newValue) {
console.log('resolve')
value = newValue;
state = 'resolved';
if(deferred) {
console.log('deferred')
handle(deferred);
}
}
function handle(handler) {
if(state === 'pending') {
deferred = handler;
return;
}
if(!handler.onResolved) {
handler.resolve(value);
return;
}
var ret = handler.onResolved(value);
handler.resolve(ret);
}
this.then = function(onResolved) {
return new Promise(function(resolves) {
console.log(resolves)
handle({
onResolved: onResolved,
resolve: resolve
});
});
};
fn(resolve);
}
function doSomething () {
return new Promise(function(resolve) {
console.log('doSomeThing')
var value = 42;
resolve(value);
});
}
doSomething().then(function(result) {
console.log('first result', result);
return 88;
}).then(function(secondResult) {
console.log('second result', secondResult);
});
// * 所有操作都在doSomething 的 Promsie中操作.在第一个then时在Promise对象生成新的promise对象,第二个then在第一个生成的promise中操作
进入 Promise(doSomething)
生成 Promise(第一个then)
生成 Promise(第二个then)
// 1. doSomething中 promise对象传入function进入Promise对象中,执行 fn(resolve) 执行到resolve(value)时,调用resolve方法
function fn (function resolve(value) {
console.log('resolve')
value = newValue;
state = 'resolved';
if(deferred) {
handle(deferred);
}
})(42)
// 2. 赋值value到Promise对象的变量中
// 3. 调用this.then方法, 将
// function(result) {
// console.log('first result', result);
// return 88;
// }
// 作为参数传入then方法中,此时创建新的Promise(第一个then)
// 4. 调用handle方法,传入{ onResolved: onResolved, resolve: resolve }参数,此时resolve为Promise(doSomething)对象中的resolve方法
// 5. 在handle 中执行 var ret = handler.onResolved(value); 此时value为Promise(doSomething)对象的变量
// 因为resolve为Promise(doSomething)中的function,resolve中的value因为闭包的原因使得Promise(第一个then)中的value
// 指向Promise(doSomething)的value,value值被保留下来
// * 注意
// 此时的value不是this.value, this.value指向当前对象, 改为this.value后将会undefined,因为Promise(第一个then)对象的value申明但没有定义
// ret 为 then中return返回的88
// 6. 调用 handler.resolve(ret);将88赋值给value,返回新的Promise对象(第一个then)
// 7. 之后的then重复上述步骤
this.then = function (onResolved)
return new Promise(function(resolve) {
console.log(resolves)
handle({
onResolved: onResolved,
resolve: resolve
});
}
)