Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

4.promise中的其它方法 #25

Open
Zijue opened this issue Jun 22, 2021 · 0 comments
Open

4.promise中的其它方法 #25

Zijue opened this issue Jun 22, 2021 · 0 comments
Labels

Comments

@Zijue
Copy link
Owner

Zijue commented Jun 22, 2021

延迟对象解决嵌套问题

在上一节中为了验证我们手写的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
}

改完后的代码少了一层嵌套,好像也没什么用,但是这是一种思想。

catchPromise.resolvePromise.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静态方法
    rejectresove实现原理一样,只是不具备等待功能,代码如下:
class Promise {
    ...
    static reject(err) {
        return new Promise((resolve, reject) => {
            reject(err);
        })
    }
    ...
}

Promise.allfinally

  • 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实现。
@Zijue Zijue added the promise label Jun 22, 2021
@Zijue Zijue changed the title 4.promise中的其他方法 4.promise中的其它方法 Jul 3, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant