一、es6增加了对Promise对象的支持,允许你对延时和异步操作流进行控制,是异步编程的一种解决方案,比传统的方案(回调函数和事件)更加的合理和强大。
1、异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。
2、异步函数调用
| 【示例】创建一个名为createAudioFileAsync()的函数,接收一些配置和两个回调函数,然后异步地生成音频文件。一个回调函数在文件成功创建时被调用,另一个在出现异常时被调用。```javascript // 成功的回调函数 function successCallback(result) { console.log(“音频文件创建成功: “ + result); }
// 失败的回调函数 function failureCallback(error) { console.log(“音频文件创建失败: “ + error); }
createAudioFileAsync(audioSettings, successCallback, failureCallback)
1、更现代的函数会返回一个Promise对象,使得你可以将你的回调函数绑定在该Promise上。<br />2、如果函数createAudioFileAsync()被重写为返回Promise的形式,那么我们可以像下面这样简单地调用它```javascript
const promise = createAudioFileAsync(audioSettings);
promise.then(successCallback, failureCallback); // 异步函数调用
或简写为```javascript createAudioFileAsync(audioSettings).then(successCallback, failureCallback); // 异步函数调用
|
| --- |
二、promise可以解决异步的问题,本身不能说promise是异步的。
<a name="yrfql"></a>
# 语法
一、Promise 对象的构造器(constructor)语法如下:
```javascript
let promise = new Promise(function(resolve, reject) {
// executor(生产者代码,“歌手”)
});
1、传递给new Promise的函数被称为executor。
(1)当new Promise被创建,executor 会自动运行。
(2)它包含最终应产出结果的生产者代码
2、它的参数resolve和reject是由 JavaScript 自身提供的回调。我们的代码仅在 executor 的内部。
3、当 executor 获得了结果,无论是早还是晚都没关系,它应该调用以下回调之一:
(1)resolve(value)— 如果任务成功完成并带有结果value。
(2)reject(error)— 如果出现了 error,error即为 error 对象。
① 如果什么东西出了问题, executor 应该调用reject。这可以使用任何类型的参数来完成(就像resolve一样)。但是建议使用Error对象(或继承自Error的对象)。
4、所以总结一下就是:executor 会自动运行并尝试执行一项工作。尝试结束后,如果成功则调用resolve,如果出现 error 则调用reject。
用法
基础用法
一、创建Promise实例时,必须传入一个函数作为参数
new Promise(() => {})
new Promise() // 报错
1、该函数可以接收另外两个由JavaScript引擎提供的函数,resolve和reject。
2、函数作用
(1)resolve
将Promise对象的状态从pending变为resolved,将异步操作的结果,作为参数传递出去。
(2)reject
将Promise对象的状态从pending变为rejected,将异步操作报出的错误,作为参数传递出去。
let promise = new Promise((resolve, reject) => {
// do something
if (true) {
resolve('value')
} else {
reject('error')
}
})
3、Promise实例生成之后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
promise.then(
value => {
// resolved时调用,value为resolve函数返回的参数
console.log(value)
},
err => {
// rejected时调用,err为reject函数返回的参数
console.log(err)
}
)
4、当then方法只有一个函数参数时,此时为resolved状态的回调方法
promise.then(value => {
// 只有状态为resolved时才能调用,如果返回的是rejected状态,则报错Uncaught(in promise) error
console.log(value)
})
(1)只有当promise的状态变为resolved或者rejected时,then方法才会被调用。
5、Promise新建后就会立即执行,并且调用resolve或reject后不会终结Promise的参数函数的执行
| 【示例】```javascript let promise = new Promise(function(resolve) { console.log(‘Promise’) resolve() console.log(‘!!!’) })
promise.then(funtion() {
console.log(‘resolved’)
})
console.log(Hi!)
// Promise // !!! // Hi! // resolved
|
| --- |
6、Resolve/reject 可以立即进行<br />(1)实际上,executor 通常是异步执行某些操作,并在一段时间后调用resolve/reject,但这不是必须的。我们还可以立即调用resolve或reject,就像这样:
| 【示例】```javascript
let promise = new Promise(function(resolve, reject) {
// 不花时间去做这项工作
resolve(123); // 立即给出结果:123
});
| | —- |
(2)例如,当我们开始做一个任务时,但随后看到一切都已经完成并已被缓存时,可能就会发生这种情况。
| 【示例】模拟一个请求,点击按钮,调用请求10s后才返回成功信息```json // func:mock模拟请求 const mockAjax = () => { return new Promise((resolve, reject) => { setTimeout(() => { resolve({ code: 200, message: ‘抢购成功’ }) }, 1000) }) }
// func:点击抢购按钮
const toBuy = () => {
if(seconds > 0) return;
mockAjax().then((res) => {
if (res.code === 200) {
setBtnText('已抢购');
}
})
}
|
| --- |
<a name="u9oKO"></a>
## resolve返回的是另外一个Promise实例
| 【示例】```javascript
const p1 = new Promise((_, reject) => {
setTimeout(() => reject('error'), 3000);
});
const p2 = new Promise(resolve => {
setTimeout(() => resolve(p1), 1000);
});
p2.then(
result => console.log(result),
error => console.log(error) // 控制台打印出:error
);
1、p1是一个Promise,3秒后变为rejected。
2、p2的状态在1秒之后改变,resolve方法返回的是p1
3、由于p2返回的是另一个Promise,导致p2自己的状态无效了,由p1的状态决定p2的状态。
4、所以,后面的then语句都变成针对p1。
5、又过了2秒,p1变为rejected,导致触发catch方法指定的回调函数。
总结:p2.then 实际上是p1.then |
| —- |
Promise对象的属性、状态
Promise对象的内部属性
一、由new Promise构造器返回的promise对象具有以下内部属性:
- state— 最初是”pending”,然后在resolve被调用时变为”fulfilled”,或者在reject被调用时变为”rejected”。
- result— 最初是undefined,然后在resolve(value)被调用时变为value,或者在reject(error)被调用时变为error。
所以,executor 最终将promise移至以下状态之一:
二、Promise 对象的state和result属性都是内部的。我们无法直接访问它们。但我们可以对它们使用.then/.catch/.finally方法。
Promise对象的状态
一、Promise对象有以下几种状态
1、pending:初始的状态,即正在执行,不处于fulfilled或rejected状态
2、fulfilled:成功地完成了操作。
3、rejected:失败,没有完成的操作。
4、settled:Promise处于fulfilled或rejected二者中的任意一个状态,不会是pending
Promise特点
一、promise有以下特点:
- 对象的状态不受外界影响
- 一旦状态改变,就不会再变
- promie内部发生错误,不会影响到外部程序的进行。
- 无法取消Promise
二、对象的状态不受外界影响。
1、Promise对象代表一个异步操作,有三种状态:pending(进行中)、resolved(已成功)和rejected(已失败)。
三、一旦状态改变,就不会再变,任何时候都可以得到这个结果。
1、Promise对象的改变,只有两种可能:从pending变为resolved和从pending变为rejected
| 【示例】所有其他的再对resolve和reject的调用都会被忽略:```javascript let promise = new Promise(function(resolve, reject) { resolve(“done”);
reject(new Error(“…”)); // 被忽略 setTimeout(() => resolve(“…”)); // 被忽略 });
(1)resolve/reject只需要一个参数(或不包含任何参数),并且将忽略额外的参数。 |
| --- |
四、promie内部发生错误,不会影响到外部程序的进行。<br />五、无法取消Promise<br />1、一旦新建它就会立即执行,无法中途取消。<br />2、如果不设置回到函数,Promise内部抛出的错误,不会反应到外部。<br />3、当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)
<a name="zdd6b"></a>
# 约定
一、不同于”老式“的传入回调,在使用Promise时,会有以下约定:<br />1、在本轮事件循环运行完成之前,回调函数是不会被调用的。<br />2、即使异步操作已经完成(成功或失败),在这之后通过then()添加的回调函数也会被调用。<br />3、通过多次调用then()可以添加多个回调函数,它们会按照插入顺序进行执行。
<a name="Cjadi"></a>
# 在旧式回调API中创建Promise
一、可以通过Promise的构造器从零开始创建Promise。这种方式(通过构造器的方式)应当只在封装旧API的时候用到。<br />二、理想状态下,所有的异步函数都已经返回Promise了。但有一些API仍然使用旧方式来传入的成功(或者失败)的回调。
| 【示例】setTimeout()函数```javascript
setTimeout( () => saySomething('10 seconds passed', 10000))
| | —- |
三、混用旧式回调和Promise可能会造成运行时序问题。
1、如果saySomething函数失败了,或者包含了编程错误,那就没有办法捕获它了。(setTimeout的问题)
2、我们可以用Promise来封装它。最好的做法是,将这些有问题的函数封装起来,留在底层,并且永远不要再直接调用它们
const wait = ms => new Promsie(resolve => setTimeout(resolve, ms))
wait(10000).then(() => saySomething('10 seconds')).catch(failure Callback)
Promise的构造器接收一个执行函数(executor),我们可以在这个执行函数里手动地resolve和reject一个Promise。既然setTimeout并不会真的执行失败,那么我们可以在这种情况下忽略reject。
Promise API
【见】Promise API: https://www.yuque.com/webfront/js/aooww6
实际使用场景
通过XHR加载图片
| 【示例】使用了Promise和XMLHttpRequest来加载一张图片,展现了Promise的工作流```javascript function imgLoad(url) { return new Promise(function(resolve, reject) { var request = new XMLHttpRequest(); request.open(‘GET’, url); request.responseType = ‘blob’; request.onload = function() { if (request.status === 200) { resolve(request.response); } else { reject(Error(‘Image didn\’t load successfully; error code:’
+ request.statusText));
}
};
request.onerror = function() {
reject(Error('There was a network error.'));
};
request.send();
}); } ``` | | —- |