Promise(基础)
Promise是什么
1.promise是一门新的技术(ES6规范)
2.Promise是JS中一编程的解决方案(旧的解决方案是单纯的使用回调函数)
3.promise一个构造函数,promise队形用来封装一个一步操作并可以获取其成功/失败的结果值。
注常见的异步操作:文件操作、数据库操作、AJAX、定时器
那么promise有哪些优势呢?
1.支持链式调用
2.解决地狱回调的问题(地狱回调就是,在回调中嵌套回调),不易于代码的维护。
promise hello world
// 普通的异步任务
<body>
<button id="btn">点击抽奖</button>
</body>
<script>
const but = document.getElementById("btb");
btn.addEventListener('click', function () {
setTimeout(() => {
const n = Math.random(1, 100);
if (n <= 0.3) alert("中奖了");
else alert("没有中奖");
}, 1000)
})
</script>
promise写法
// 用promise包裹异步操作
<body>
<button id="btn">点击抽奖</button>
</body>
<script>
const but = document.getElementById("btb");
btn.addEventListener('click', function () {
// 参数为函数类型
const p = new Promise((resolve, reject) => {
setTimeout(() => {
const n = Math.random(1, 100);
if (n <= 0.3) resolve();
else reject();
}, 1000)
});
// 指定回调,成功执行resolve函数,执行失败执行reject函数
p.then(() => {
alert("中奖了");
}, () => {
alert("没有中奖")
})
})
</script>
如果要传递参数
const p = new Promise((resolve, reject) => {
setTimeout(() => {
const n = Math.random(1, 100);
if (n <= 0.3) resolve(n);
else reject(n);
}, 1000)
});
// 指定回调时接受参数
p.then((val) => {
alert("中奖了" + val);
}, (val) => {
alert("没有中奖" + val)
})
promise基础实践(熟悉写法)
- 文件操作(需要node环境)
// 普通代码
const fs = require('fs');
fs.readFile('./data.txt', (err, data) => {
if (err) console.log(err);
else console.log(data);
})
promise封装
// promise封装
const fs = require('fs');
const p = new Promise((resolve, reject) => {
fs.readFile('./data.txt', (err, data) => {
if (err) reject(err);
else resolve(data);
})
})
p.then((data) => {
console.log("读取文件成功!!!")
console.log(data)
}, (err) => {
console.log("读取文件成功!!!")
console.log(err)
})
- ajax封装
// 原始写法
var XMLHttpRequest = require('xhr2');
function send() {
const xhr = new XMLHttpRequest();
xhr.open("GET", "http://127.0.0.1:8888");
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status <= 300) {
console.log("成功!!!")
console.log(xhr.response)
} else {
console.log("失败!!!")
console.log(xhr.status)
}
}
}
}
send()
// promise写法
const p = new Promise((resolve, reject) =>{
const xhr = new XMLHttpRequest();
xhr.open("GET", "http://127.0.0.1:8888");
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status <= 300) {
console.log("成功!!!")
resolve(xhr.response);
} else {
console.log("失败!!!")
reject(xhr.status);
}
}
}
})
p.then((data) => {
console.log(data)
}, (err) => {
console.log(err)
})
Promise知识点
- util.promisify promise对象转换
const fs = require("fs")
const util = require("util");
// 将回调函数转换为promise对象
const preadFile = util.promisify(fs.readFile)
preadFile("./data.txt").then((data) => {
console.log(data.toString())
});
- promise的状态改变
promise实例的[promiseState]
- pendging 未决定的
- resolved/fullfilled 成功
- rejected 失败
promise的状态改变只有两种情况:
pending 变为resolved
pending 变为rejected
-
promise 对象的值
实实例属性[promiseResult] 保存着对象[成功/失败]的结果
只有resolve和reject函数可以修改这个结果值 -
promise的工作流程
-
Promise API
1)Promise构造函数 Promise(excutor) {}
executor函数 : 执行器(resolve, reject) => {}
resolve函数 : resolve
reject函数 : reject
2)Then方法 (onResolved, onRejected) => {}
onRedolved函数 : 成功的回调函数
onRejected函数 : 失败的回调函数
3)Promise.prototype.catch 方法 (onrejected)=> {}
onRejected 函数:失败的回到
注:这个方法只能指定失败的回调函数
4)Promise.resolve()
// 如果resolve中传入的参数为非promise对象则返回的结果为resolve的结果,成功的结果为参数
// 如果传入的参数为promise对象结果取决于参数的结果
let p = Promise.resolve(521)
console.log(p)
let p = new Promise(new Promise((resolve, reject) => {}));
5)Promise.reject()
// 如果resolve中传入的参数为非promise对象则返回的结果为reject的结果,失败的结果就是参数
// 如果传入的参数为promise对象结果取决于参数的结果
let p = Promise.reject(521)
console.log(p)
let p = new Promise(new Promise((resolve, reject) => {}));
6)Promise.all()
包含n个promise数组,结果返回一个新的promise,只有所以的promise都成功才成功,只要有一个失败就是败。
let p1 = new Promise((resolve, reject) => {
resolve('ok')
})
let p2 = Promise.resolve("ok");
let p3 = Promise.reject("error");
const res = Promise.all([p1, p2, p3])
console.log(res)
7)Promise.race()
返回一个新的promise对象,第一个完成的promise的状态就是最终的状态。
应用场景:比如提供了多个接口,效率不一样谁先返回用谁的结果。
8)改变promise的状态
初始状态为pending,之后可以调用resolve和reject改变状态,throw爆出错误也可以改变promise的状态。
9)Promise指定多个成功回调会执行吗
let p = new Promise((resolve, reject) => {
resolve('ok')
})
p.then(v => {
console.log(v)
})
p.then(v => {
console.log(v + 1)
})
// 验证可知多个回调都会执行,如果Promise一直为pending状态则不会执行
10)改变状态和指定函数回调谁先谁后?
都有可能
如果promise中是同步任务则是先改变状态,然后指定回调。如果是异步任务则可能是先指定回调然后再改变状态
11)then链式调用
let p = new Promise((resolve, reject) => {
resolve('ok')
})
p.then(() =>{
return new Promise((resolve, reject) => {
resolve("success")
})
}).then(v => {
console.log(v)
}).then(v => {
// 输出undifined,因为上一个thenPromise的返回结果没有写
console.log(v)
})
12)异常穿透
通过then链式调用的时候发生错误可以在最终来进行指定回调来处理。
let p = new Promise((resolve, reject) => {
resolve('ok')
})
p.then(() =>{
return new Promise((resolve, reject) => {
reject("err")
})
}).then(v => {
console.log(v)
}).then(v => {
// 输出undifined
console.log(v)
}).catch((err) => {
console.log(err)
})
13)终端promise链
需要在then方法中返回一个pending状态的promise才能终端promise链
Promise 自定义封装
此部分需要多写多练
class Promise {
constructor(executor) {
this.promiseState = "pending"
this.promiseResult = null
// 用于保存异步任务的回调函数
this.callbacks = []
// 这里需要注意this问题
// 可以先存下来self = this 后面用self替代this
const self = this;
function resolve(data) {
// 保证状态只改变一次
if (self.promiseState !== "pending") return
self.promiseState = 'resolved'
self.promiseResult = data
// 指定多个回调
// 使得它进入异步队列,用定时器包裹
setTimeout(() => {
self.callbacks.forEach(item => {
item.onResolved(data);
})
})
}
function reject(data) {
if (self.promiseState !== "pending") return
self.promiseState = 'rejected'
self.promiseResult = data
// 指定多个回调
setTimeout(() => {
self.callbacks.forEach(item => {
item.onRejected(data);
})
})
}
try {
executor(resolve, reject);
} catch (err) {
// 直接调用reject函数改变状态为rejected
reject(err)
}
}
then(onResolved, onRejected) {
const self = this;
// 异常穿透的逻辑
if (typeof onRejected !== "function") {
onRejected = reason => {throw reason};
}
if (typeof onResolved !== "function") {
onResolved = value => value;
}
return new Promise((resolve, reject) => {
function callback(method) {
try {
let result = method(self.promiseResult)
// 如果返回的是promise对象,promise的结果取决于这个promise的结果
if (result instanceof Promise) {
result.then(v => {
resolve(v);
}, e => {
reject(e)
})
} else {
// 如果返回的是值,promise的结果就是这个值
resolve(result)
}
} catch (e) {
reject(e)
}
}
if (this.promiseState === "resolved") {
setTimeout(() => {
callback(onResolved)
})
} else if (this.promiseState === "rejected") {
setTimeout(() => {
callback(onRejected)
})
} else {
// 这里异步任务先指定回调,由于可以指定多个回调所以用数组保存
this.callbacks.push({
onResolved: function () {
callback(onResolved)
},
onRejected: function () {
callback(onRejected)
}
});
}
})
}
catch(onRejected) {
return this.then(undefined, onRejected)
}
static resolve(value) {
return new Promise((resolve, reject) => {
if (value instanceof Promise) {
value.then(v => {
resolve(v)
},
e => {
reject(e)
})
} else {
resolve(value)
}
})
}
static reject(reason) {
return new Promise((resolve, reject) => {
reject(reason);
})
}
static all(promise) {
return new Promise((resolve, reject) => {
let count = 0;
const res = [];
for (let i = 0; i < promise.length; i++) {
promise[i].then((v) => {
res[i] = v
count++;
}, (r) => {
reject(r)
})
if (count == promise.length) {
resolve(res);
}
}
})
}
static race(promise) {
return new Promise((resolve, reject) => {
for (let i = 0; i < promise.length; i++) {
promise[i].then((v) => {
resolve(v)
}, (r) => {
reject(r)
})
}
})
}
}
参考视频:
Promise教程