4.promise中的其它方法
Opened this issue · 0 comments
Zijue commented
延迟对象解决嵌套问题
在上一节中为了验证我们手写的promise
是否符合规范,在手写的Promise
类上添加了Promise.deferred
方法。这个deferred
是一种编程**,可以用于解决部分嵌套问题。比如下面这段代码:
function readFile(...args) {
return new Promise((resolve, reject) => {
fs.readFile(...args, (err, data) => {
if (err) return reject(err);
resolve(data);
})
})
}
使用deferred
可以将代码改写为:
function readFile(...args) {
let dfd = Promise.deferred();
fs.readFile(...args, (err, data) => {
if (err) return dfd.reject(err);
dfd.resolve(data);
});
return dfd.promise
}
改完后的代码少了一层嵌套,好像也没什么用,但是这是一种**。
catch
、Promise.resolve
、Promise.reject
catch
方法
经常在写Promise
时,需要捕获错误,但是then
方法中需要传递两个参数;这时就可以用catch
方法只捕获错误,同时后面可以继续then
readFile('./xxx.txt', 'uft8').then(data => {
console.log('data');
}).catch(err => {
console.log(err);
}).then(data=>{
console.log('continue'); // 此处会继续执行
})
说白了,catch
就是一个没有成功回调方法的then
方法,实现代码如下:
class Promise {
...
catch(errFn) {
return this.then(null, errFn);
}
...
}
Promise.resolve
静态方法
有时我们需要直接返回一个成功态的promise
,这个时候我们就可以使用Promise.resolve
;同时Promise.resolve
还具备等待效果,如果我们传入的是一个promise
,那么它会将该promise
成功或失败的结果向下传递。先来实现传入普通值:
class Promise {
...
static resolve(value) {
return new Promise((resolve, reject) => {
resolve(value);
})
}
...
}
假如我们传入一个Promise
,那么结果会是这样:
Promise {
status: 'FULFILLED',
value: 'ok',
reason: undefined,
onFulfilledCallbacks: [],
onRejectedCallbacks: []
}
我们写的代码出现这个的原因就是Promise
中定义的resolve
函数直接将传入的value
赋给了promise
实例,并将此值作为成功结果执行,因此需要对resolve
函数进行修改:
class Promise {
...
const resolve = (value) => {
if (value instanceof Promise) { // 这个方法并不属于规范中的,只是为了和原生promise表现形式一样
return value.then(resolve, reject);
}
if (this.status == PENDING) {
this.value = value;
this.status = FULFILLED;
this.onFulfilledCallbacks.forEach(fn => fn());
}
};
...
}
Promise.resolve
静态方法
reject
与resove
实现原理一样,只是不具备等待功能,代码如下:
class Promise {
...
static reject(err) {
return new Promise((resolve, reject) => {
reject(err);
})
}
...
}
Promise.all
与finally
Promise.all
:表示全部成功才成功,如果有一个失败则失败
核心原理代码如下:
Promise.all = function (promises) {
return new Promise((resolve, reject) => {
let results = [];
let index = 0;
function process(v, k) { // 与after函数实现原理一致
results[k] = v;
if (++index == promises.length) { // 解决多个异步并发问题,只能靠计数器
resolve(results);
}
}
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);
} else {
process(p, i); // 同步的
}
}
})
}
finally
:无论成功和失败都会执行的方法finally
如果返回的是一个promise,那么会有等待效果- 只有返回一个失败态的promise,才会将返回的promise失败的原因向下传递,否则传递
finally
之前的成功结果或失败原因
核心原理代码如下:
Promise.prototype.finally = function (cb) {
return this.then((y) => {
return Promise.resolve(cb()).then((d) => y);
}, (r) => {
// cb执行一旦报错 就直接跳过后续的then的逻辑,直接将错误向下传递
return Promise.resolve(cb()).then(() => { throw r })
})
}
如何将不是Promise的异步API转换成Promise
function promisify(fn) { // 高阶函数
return function (...args) {
return new Promise((resolve, reject) => {
fn(...args, function (err, data) { // node 所有的api第一个参数都是error
if (err) return reject(err);
resolve(data);
})
})
}
}
// 测试promisify方法
const fs = require('fs');
let read = promisify(fs.readFile);
read('z1.txt', 'utf8').then(data => {
console.log(data)
});
promisify
可以将所有的回调方法转化成promise,node的api可以使用.promises
的方式引入promise的异步方法:
const fs = require('fs').promises;
Promise.race
静态方法
race
方法,调用的列表中任何一个成功或失败,就采用它的结果。实现原理如下:
Promise.race = function (promises) {
return new Promise((resolve, reject) => {
for (let promise of promises) {
if (promise && typeof promise.then == 'function') {
promise.then(resolve, reject)
} else {
resolve(promise);
}
}
})
}
- 使用
race
方法可以实现promise版的具有超时功能的图片懒加载,如下:
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('图片加载完成');
}, 3000);
});
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('请求超时');
}, 2000);
})
Promise.race([p1, p2]).then(data => {
console.log(data);
}, err => {
console.log(err);
})
// 执行结果
$ 请求超时 -- 2s左右
-- 3s左右,程序才结束
promise是没法中断执行的,无论如何都会执行完毕,只是不采用这个promise的成功或失败的结果了。
- 如何在不改变promise原有代码的前提下,提供一个
abort
中断方法
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('图片加载完成');
}, 3000);
});
function wrap(old) {
let abort;
let p2 = new Promise((resolve, reject) => {
abort = reject; // 内置了一个promise,我们可以控制这个promise,来影响Promise.race的结果
})
let returnPromise = Promise.race([old, p2])
returnPromise.abort = abort;
return returnPromise
}
let newPromise = wrap(p1);
setTimeout(() => {
newPromise.abort('超时 2000');
}, 2000);
newPromise.then(data => {
console.log(data);
}, err => {
console.log(err)
});
原理就是通过切片编程的**,返回一个新的promise,通过内置的promise影响Promise.race
的结果
- promise是没法中断执行的,但是可以中断链式调用:通过返回一个
PENDING
状态的Promise
Promise.resolve('1').then(data => {
console.log(data);
return new Promise(() => { }); // 返回一个promise,会采用他的状态;如果不成功也不失败,就不会向下执行了
}).then((data) => {
console.log(data)
});
- 小结:如果希望不采用原有的结果,可以通过
Promise.race
;如果希望中断promise的链式调用,则需要返回一个pending状态的promise实现。