Promise.deferred
- 原生没有这个方法
// Promise.deferred 可以减少一层promise嵌套 原生没有这个方法
// 用promise包了一层
function readFile(...args) {
return new Promise((resolve, reject) => {
fs.readFile(...args, (err,data) => {
if(err) return reject(err);
resolve(data);
})
})
}
Promise.deferred = function() {
let dfd = {};
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
})
return dfd;
};
function readFile(...args) {
let dfd = Promise.deferred(); //创建了一个promise
fs.readFile(...args, function(err, data) {
if(err) return dfd.reject(err);
dfd.resolve(data);
})
return dfd.promise; // 返回promise实例
}
catch
readFile("./xxx").then(data => {
console.log(data);
})
.catch(err => {
console.log(err);
})
// catch捕获错误,后面可以继续调用then方法
// promise实例方法
class Promise {
catch(errFn) {
return this.then(null, errFn)
}
}
静态方法
- Promise.resolve
- Promise.reject
Promise.resolve("hello").then(data => {
console.log(data); //'hello'
});
Promise.reject("hello").catch(err => {
console.log(err); // 'hello'
});
// 两者的差异在于resolve具有等待的效果
// 与上面的不同是传递的字符串改成promise实例
Promise.resolve(new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hello')
}, 1000)
})).then(data => {
console.log(data); // 等待promise执行完成, 输出‘hello’
});
Promise.reject(new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hello')
}, 1000)
})).catch(err => {
console.log(err); // promise{ <pending> } 不等待
});
// 实现
class Promise{
constructor(executor) {
const resolve = (value) => {
// 传入的value有可能是promise
// 这里增加的逻辑不属于规范中,只是为了和原生的promise表现形式一样
if(value instanceof Promise) {
return value.then(resolve, reject);
}
}
}
static resolve(value) {
return new Promise((resolve, reject) => {
resolve(value);
})
}
static reject(err) {
return new Promise((resolve, reject) => {
reject(err);
})
}
}
- Promise.all
- 原型方法finally
const fs = require("fs").promises;
Promise.all([fs.readFile("name.txt", "utf8"), fs.readFile("age.txt", "utf8")])
.then((data) => {
console.log(data);
})
.catch((err) => {
console.log(err);
});
// Promise.all 表示全部成功才成功 一个失败就失败
// Promise.allSettled 无论成功失败都拿到全部结果
Promise.all = function (promises) {
return new Promise((resolve, reject) => {
let result = [];
let index = 0;
function process(value, key) {
result[key] = value;
if (++index === promises.length) {
// 靠计时器解决多个异步并发问题
resolve(result);
}
}
for (let i = 0; i < promises.length; i++) {
let p = promises[i];
if (p && typeof p.then === "function") {
p.then((data) => {
// 异步
process(data, i);
}, reject); // 如果有一个promise失败了 就执行最后的失败逻辑
} else {
process(p, i); // 同步
}
}
});
};
/**
* 1.finally 无论成功失败都会执行的方法 并且可以继续调用then
* data可以穿透传递到下一个then
* 2. finally里的回调如果返回的是一个promise,会有等待效果
* 会等待promise执行完毕,再将上一个结果传到下面的then中去
* 如果promise失败 会将失败的promise参数传递给下一个then的失败回调中作为参数
*/
Promise.resolve("ok")
.finally(() => {
// finally里的函数是没有参数的,会把上面的参数传递到下面的then中
console.log("无论成功失败都会调用")
})
.then((data) => {
console.log('成功', data);
})
.catch((err) => {
console.log('失败', err);
})
Promise.resolve("ok")
.finally(() => {
// finally里的函数是没有参数的,会把上面的参数传递到下面的then中
console.log("无论成功失败都会调用")
return new Promise((resolve, reject) => {
setTimeout(()=> {
resolve("XXXXX");
}, 1000)
})
})
.then((data) => {
console.log('成功', data);
})
.catch((err) => {
console.log('失败', err);
})
Promise.prototype.finally = function(cb) {
return this.then((y) => {
// cb()
// return y;
// cb()执行完可能是一个promise,并且有等待效果 不管是不是 统一包装成promise
// 不用Promise.reject()包裹是因为它不具备等待效果
// 参数d是“XXXXX” y是“ok”
// cb执行一旦报错,就直接跳过后续的then的执行逻辑,直接将错误向下传递
return Promise.resolve(cb()).then(d => y)
},(r) => {
// cb();
// throw r;
return Promise.resolve(cb()).then(d => { throw r })
})
}
- 异步方法转换成promise形式
// 1.可以将所有的原有api变成promise的形式
const fs = require("fs").promises;
// 2. 单独将某一个方法转换成promise的形式
const fs = require("fs");
const util = require("util"); // node中的另一个基础模块(工具方法)
util.promisify(fs.readFile)
// 使用
let read = util.promisify(fs.readFile);
read("note.md", "utf8").then(data => {
console.log(data);
})
// 涉及到如何将不是promise的异步api转换成promise
function promisify(fn) {
return function(...args) {
return new Promise((resolve, reject) => {
fn(...args, (err, data) => {
if(err) return reject(err);
resolve(data);
})
})
}
}
function promisifyAll(obj) {
for(let key in obj) {
if(typeof obj[key] === 'function') {
obj[key] = promisify(obj[key]);
}
}
return obj;
}
- Promise.race
// 使用
Promise.race([fs.readFile("a.md", "utf8"), fs.readFile("b.md","utf8")]).then(data => {
console.log(data)
}, err => {
console.log(err);
});
// 实现
Promise.race = function(promises) {
return new Promise((resolve, reject) => {
for(let i= 0; i < promises.length; i++) {
let p = promises[i];
if( p && typeof p.then === 'function'){
p.then(resolve, reject);
}else{// 不是promise
resolve(p);
}
}
})
}
- Promise.race的一个应用:请求超时
// 模拟获取图片的promise
let p = new Promise((resolve, reject)=> {
setTimeout(() => {
resolve("图片加载成功")
}, 3000);
});
function wrap(oldPromise) {
let abort;
// 增加一个promise(p2), 可以决定promise.race的成功和失败
let p2 = new Promise((resolve, reject) => {
abort = reject;
})
let returnedPromise = Promise.race([oldPromise, p2]);
returnedPromise.abort = abort;
return returnedPromise
}
let newPromise = wrap(p);
setTimeout(() => {
newPromise.abort("超时");
}, 2000);
newPromise.then(data => {
console.log(data)
}, err => {
console.log(err);
})
如何中断promise的链式调用
- 怎么让promise不走到下面的then
方案是:返回一个等待的promise
- 中断的两种方式
- 不采用原有的结果,可以考虑采用Promise.race
- 破坏链式调用,返回pending的promise
Promise.resolve("1").then(data => {
console.log(data);
return new Promise(()=>{}); //返回一个promise,会采用他的状态,如果不成功也不失败就不会向下执行了
}).then(data => {
console.log(data);
})
实现异步并发上限控制
class LimitPromise {
constructor(max) {
this._max = max; // 异步任务并发上限
this._count = 0; // 当前正在执行的任务数量
this._taskQueue = []; // 等待执行的任务队列
}
/**
* call 调用器 将异步任务函数和他的参数传入
* caller 异步任务函数 必须是async函数或者返回promise函数
* args 异步任务函数大的参数列表
* returns 返回一个新的promise
*/
call(caller, ...args) {
return new Promise((resolve, reject) => {
// 创建一个任务 判断任务是执行还是入队
const task = this._createTask(caller, args, resolve, reject);
if(this._count >= this._max) {
this._taskQueue.push(task);
}else{
task();
}
})
}
/**
* _createTask 创建一个任务
* caller 实际执行函数
* args 执行函数的参数
* 返回一个任务函数
*/
_createTask(caller, args, resolve, reject) {
return () => {
// 当前请求数量+1
this._count++;
// 实际上是在这里调用了异步任务,并将异步任务的返回(resolve,reject)抛给了上一层
caller(...args)
.then(resolve)
.catch(reject)
.finally(() => {
// 在finally中判断是否执行下一个任务,实现任务队列连续消费的地方就是这里
// 任务队列的消费区,利用Promise.finally方法在异步任务结束后取出下一个任务执行
this._count --;
if(this._taskQueue.length) {
let task = this._taskQueue.shift();
task()
}
})
}
}
}
// 使用
// 假设我们有一个网络请求模块,叫request.js,包含get和post方法,一般情况下,是这样使用的:
const request = require('./request')
request.get('https://www.baidu.com')
.then((res) => {
// 处理返回结果
})
.catch(err => {
// 处理异常情况
})
// 现在我们要把它改造成受限制的网络请求,假设请求上限设为10个,并起名叫limitRequest.js。实现如下:
const LimitPromise = require('limit-promise')
const request = require('./request')
// 请求上限
const MAX = 10
// 核心控制器
const limitP = new LimitPromise(MAX)
// 利用核心控制器包装request中的函数
function get (url, params) {
return limitP.call(request.get, url, params)
}
function post (url, params) {
return limitP.call(request.post, url, params)
}
// 导出
module.exports = {get, post}