使用 Promise
目录
1.Promise是什么. Promise 对象用于表示一个异步操作的最终完成 (或失败), 及其结果值.
2.约定(你可以先跳过这里,因为你最好在了解他的运行顺序再来看这里,这样你就会明白了)
2.Promise.resolve()和 Promise.reject()
讲人话就是,then(....).resolve这样的写法.
蓝字是我的重点讲解.但是建议先看完白字.结合https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Using_promises
1.Promise是什么. Promise 对象用于表示一个异步操作的最终完成 (或失败), 及其结果值.
首先来听一个故事,有一个名为 createAudioFileAsync()
的函数,如果给出一些配置和两个回调函数,这个函数能异步地生成音频文件。一个回调函数是文件成功创建时的回调,另一个则是出现异常时的回调。
// 成功的回调函数
function successCallback(result) {
console.log("音频文件创建成功: " + result);
}
// 失败的回调函数
function failureCallback(error) {
console.log("音频文件创建失败: " + error);
}
createAudioFileAsync(audioSettings, successCallback, failureCallback)
函数 createAudioFileAsync()
被重写为返回 Promise 对象的形式
const promise = createAudioFileAsync(audioSettings);
promise.then(successCallback, failureCallback);
createAudioFileAsync(audioSettings).then(successCallback, failureCallback);
// createAudioFileAsync() 是一个`重写返回Promise对象的形式
看起来就是这样他就是改了个样子,Promise 是一个被某些函数传出的对象,我们附加回调函数(callback)使用它,而不是将回调函数传入那些函数内部。(这句话很绕口,意思就是写promise底层的是个函数,我们知道promise对象默认是要传进一个function的(
new Promise( function(resolve, reject) {...} /* executor */ );
),但是这个function不是传给底层构造他的函数.而是在then()这个触发的时候调用的.总之,我看完后,觉得他真的像Node的事件模块.不过因为同步和异步的差别,他们执行的顺序还是有点差别,但是多次注册,以及多次触发,链式调用.真的写法上又有很大的相似.不过这两个还是不一样的.当然Node事件模块还是可以,调用promise的.这块待之后研究.)
2.约定(你可以先跳过这里,因为你最好在了解他的运行顺序再来看这里,这样你就会明白了)
- 在 本轮 Javascript event loop(事件循环)运行完成 之前,回调函数是不会被调用的。
- 通过
then()
添加的回调函数总会被调用,即便它是在异步操作完成之后才被添加的函数。 - 通过多次调用
then()
,可以添加多个回调函数,它们会按照插入顺序一个接一个独立执行。
3.api的讲解
p.then(onFulfilled[, onRejected]);
p.then(value => {
// fulfillment
}, reason => {
// rejection
});
1.链式调用promise.then()的语法
第一种语法:
Promise.resolve("foo")
// 1. 接收 "foo" 并与 "bar" 拼接,并将其结果做为下一个 resolve 返回。
.then(function(string) {
console.log(string)
return new Promise(function(resolve, reject) {
setTimeout(function() {
string += 'bar';
resolve(string);
}, 1);
});
})
// 2. 接收 "foobar", 放入一个异步函数中处理该字符串
// 并将其打印到控制台中, 但是不将处理后的字符串返回到下一个。
.then(function(string) {
setTimeout(function() {
string += 'baz';
console.log(string);
}, 1)
return string;
})
// 3. 打印本节中代码将如何运行的帮助消息,
// 字符串实际上是由上一个回调函数之前的那块异步代码处理的。
.then(function(string) {
console.log("Last Then: oops... didn't bother to instantiate and return " +
"a promise in the prior then so the sequence may be a bit " +
"surprising");
// 注意 `string` 这时不会存在 'baz'。
// 因为这是发生在我们通过setTimeout模拟的异步函数中。
console.log(string);
});
Promise.reject("foo")
// 1. 接收 "foo" 并与 "bar" 拼接,并将其结果做为下一个 resolve 返回。
.then(function(string) {
console.log(string)
return new Promise(function(resolve, reject) {
setTimeout(function() {
string += 'bar';
resolve(string);
}, 1);
});
(当 Promise 变成接受状态(fulfilled)时调用的函数
。该函数有一个参数,即接受的最终结果(the fulfillment value)。如果该参数不是函数,则会在内部被替换为 (x) => x
,即原样返回 promise 最终结果的函数)这段话真的很书面化,你根本不知道他说的是什么,然后我测试了下这个最终结构就是"foo",同理换成rejected就是错误的原因(UnhandledPromiseRejectionWarning: foo).这两个api后面讲真不行,你先跳过这里,但最好是硬着头皮,怀着疑问,然后在看完rejected和resolve后再来看这里.
这个链式写法,还要根据返回值.
- 返回了一个值,那么
then
返回的 Promise 将会成为接受状态,并且将返回的值作为接受状态的回调函数的参数值。 - 没有返回任何值,那么
then
返回的 Promise 将会成为接受状态,并且该接受状态的回调函数的参数值为undefined
。 - 抛出一个错误,那么
then
返回的 Promise 将会成为拒绝状态,并且将抛出的错误作为拒绝状态的回调函数的参数值。 - 返回一个已经是接受状态的 Promise,那么
then
返回的 Promise 也会成为接受状态,并且将那个 Promise 的接受状态的回调函数的参数值作为该被返回的Promise的接受状态回调函数的参数值。 - 返回一个已经是拒绝状态的 Promise,那么
then
返回的 Promise 也会成为拒绝状态,并且将那个 Promise 的拒绝状态的回调函数的参数值作为该被返回的Promise的拒绝状态回调函数的参数值。 - 返回一个未定状态(
pending
)的 Promise,那么then
返回 Promise 的状态也是未定的,并且它的终态与那个 Promise 的终态相同;同时,它变为终态时调用的回调函数参数与那个 Promise 变为终态时的回调函数的参数是相同的。
测试吧,这个了解下就好了.
可以通过errormessage去获取信息.
Promise.resolve()
.then(() => {
// 使 .then() 返回一个 rejected promise
throw new Error('Oh no!');
})
.then(() => {
console.log('Not called.');
}, error => {
console.error('onRejected function called: ' + error.message);
});
箭头函数简写
Promise.reject()
.then(() => 99, reason => 42) // onRejected returns 42 which is wrapped in a resolving Promise
.then(solution => console.log('Resolved with ' + solution));
了解下执行顺序
如果 onFulfilled
返回了一个 promise,then
的返回值就会被 Promise resolved 或者 rejected。
function resolveLater(resolve, reject) {
setTimeout(function() {
resolve(10);
}, 1000);
}
function rejectLater(resolve, reject) {
setTimeout(function() {
reject(new Error('Error'));
}, 1000);
}
var p1 = Promise.resolve('foo');
var p2 = p1.then(function() {
return new Promise(resolveLater);
});
var p3 = p1.then(function() {
return new Promise(rejectLater);
});
p2.then(function(v) {
console.log('resolved', v);
}, function(e) {
// not called
console.error('rejected', e);
});
p3.then(function(v) {
// not called
console.log('resolved', v);
}, function(e) {
console.error('rejected', e);
});
在旧式回调 API 中创建 Promise
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
wait(10000).then(() => saySomething("10 seconds")).catch(failureCallback)
到这里,你应该懂了,我前面说的他为什么和event模块这么像.原因在于promise就像注册一样.你一定要用触发函数去触发.then()是,setTimeout也是.所以按照这样分类,可以将promise模块分成两种一种是函数执行触发用的,一种是对promise进行拼接啥的,返回的还是promise,并且还可以和promise自己api函数进行调用.比如all的迭代.
const promise = doSomething();
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 = new Promise((resolve, reject) => {
console.log('初始化');
resolve();
})
上面的代码都是不正确的,只是一个逻辑.其中dosomething是需要new promise去构造的.return的都是promise对象.
就是一个链式的写法
catch的后续
new Promise((resolve, reject) => {
console.log('初始化');
resolve();
})
.then(() => {
throw new Error('有哪里不对了');
console.log('执行「这个」”');
})
.then(
()=>{
console.log("会不会被牵连?")
}
).catch(() => {
console.log('执行「那个」');
})
.then(() => {
console.log('执行「这个」,无论前面发生了什么');
});
通常,一遇到异常抛出,Promise 链就会停下来,直接调用链式中的 catch
处理程序来继续当前执行。这看起来和以下的同步代码的执行很相似。"会不会被牵连"就是那样的.
2.Promise.resolve()
和 Promise.reject()
首先注意几点
1.Promise.resolve(value)
方法返回一个以给定值解析后的Promise
对象。如果该值为promise,返回这个promise;如果这个值是thenable(即带有"then" 方法
))这将导致无限递归,因为它试图展平无限嵌套的promise。
讲人话就是,then(....).resolve这样的写法.
Promise.resolve(value);语法
返回一个解析过带着给定值的Promise
对象,如果参数是一个Promise
对象,则直接返回这个Promise
对象。
value(1.将被Promise
对象解析的参数。2.一个Promise
对象 3.一个thenable。)
2.Promise.reject(reason);
reason:表示Promise
被拒绝的原因。
返回值;一个给定原因了的被拒绝的 Promise
。
3. Promise.all(iterable);
iterable
Promise.all(iterable)
方法返回一个 Promise
实例,此实例在 iterable
参数内所有的 promise
都“完成(resolved)”或参数中不包含 promise
时回调完成(resolve);如果参数中 promise
有一个失败(rejected),此实例回调失败(reject),失败原因的是第一个失败 promise
的结果。
- 如果传入的参数是一个空的可迭代对象,则返回一个已完成(already resolved)状态的
Promise
。 - 如果传入的参数不包含任何
promise
,则返回一个异步完成(asynchronously resolved)Promise
。注意:Google Chrome 58 在这种情况下返回一个已完成(already resolved)状态的Promise
。 - 其它情况下返回一个处理中(pending)的
Promise
。这个返回的promise
之后会在所有的promise
都完成或有一个promise
失败时异步地变为完成或失败。 见下方关于“Promise.all 的异步或同步”示例。返回值将会按照参数内的promise
顺序排列,而不是由调用promise
的完成顺序决定。
var p1 = Promise.resolve(3);
var p2 = 1337;
var p3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.all([p1, p2, p3])
.then(([r1, r2, r3]) => {console.log(r1);console.log(r1,r2,r3)});
这样单独传参.
var p1 = Promise.resolve(3);
var p2 = Promise.resolve(312);
var p3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
[p1, p2, p3].reduce((p, f) => p.then(f), Promise.resolve(32)).then(result3 => { console.log(result3);})
const applyAsync = (acc,val) => acc.then(val);
const composeAsync = (...funcs) => x => funcs.reduce(applyAsync, Promise.resolve(x));
const transformData = composeAsync(func1, func2, func3);
const result3 = transformData(data);
砂锅面这两种需要比较下.
4.Promise.race(iterable);
iterable
一个待定的 Promise
只要给定的迭代中的一个promise解决或拒绝,就采用第一个promise的值作为它的值,从而异步地解析或拒绝(一旦堆栈为空)。
race
函数返回一个 Promise
,它将与第一个传递的 promise 相同的完成方式被完成。它可以是完成( resolves),也可以是失败(rejects),这要取决于第一个完成的方式是两个中的哪个。
3. 时序
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
wait().then(() => console.log(4));
Promise.resolve().then(() => console.log(2)).then(() => console.log(3));
console.log(1); // 1, 2, 3, 4
4.嵌套
doSomethingCritical()
.then(result => doSomethingOptional()
.then(optionalResult => doSomethingExtraNice(optionalResult))
.catch(e => {console.log(e.message)})) // 即使有异常也会忽略,继续运行;(最后会输出)
.then(() => moreCriticalStuff())
.catch(e => console.log("Critical failure: " + e.message));// 没有输出
到此为止promise 搞定了.
再来系统的看下...
一个 Promise
有以下几种状态:
- pending: 初始状态,既不是成功,也不是失败状态。
- fulfilled: 意味着操作成功完成。
- rejected: 意味着操作失败。
Promise.prototype.constructor
返回被创建的实例函数. 默认为 Promise
函数.创建Promise
const myFirstPromise = new Promise((resolve, reject) => {
// ?做一些异步操作,最终会调用下面两者之一:
//
// resolve(someValue); // fulfilled
// ?或
// reject("failure reason"); // rejected
});
上一篇: js实现模仿面板效果
下一篇: 栈的应用--计算器实现四则运算