Promise详细入门

Promise详细入门

Promise算是面试经常会问的问题,而且之前没怎么搞懂,打算花一天的时间弄懂Promise,包括他所有的方法。

一天是弄不懂了,两天也弄不懂,这个Promise很玄学,还好这篇文章是入门级别的,后续更难得我也搞不定了。

Promise是个对象,被创建出来并不知道状态。存在三种状态:

  • 待定(pending): 初始状态,既没有被兑现,也没有被拒绝。
  • 已兑现(fulfilled): 意味着操作成功完成。
  • 已拒绝(rejected): 意味着操作失败。

Promise.prototype.thenPromise.prototype.catch 方法返回的是 promise,所以可以套娃链式调用。

链式调用

可以用这些方法把promise.then()promise.catch()promise.finally() 串联起来。

const myPromise =
  (new Promise(myExecutorFunc))
  .then(handleFulfilledA,handleRejectedA)
  .then(handleFulfilledB,handleRejectedB)
  .then(handleFulfilledC,handleRejectedC);

// 或者,这样可能会更好...

const myPromise =
  (new Promise(myExecutorFunc))
  .then(handleFulfilledA)
  .then(handleFulfilledB)
  .then(handleFulfilledC)
  .catch(handleRejectedAny);

任何不是 throw 的终止都会创建一个”已决议(resolved)”状态,而以 throw 终止则会创建一个”已拒绝”状态。

.catch只是没有预留参数的.then()而已

构造函数

语法

new Promise(executor)

executor =(resolve,reject)=>{

}

这是一个双参函数,参数为resolve和rejectPromise的实现会立即执行executor,并传入resolve和reject函数(Promise构造器将会在返回新对象之前executor)。当resolvereject函数被调用时,它们分别对promise执行resolverejectexecutor通常会触发一些异步运算,一旦运算成功完成,则resolve掉这个promise,如果出错则reject掉。如果executor函数执行时抛出异常,promise状态会变为rejectedexecutor的返回值也会被忽。reject返回的通常是一个error对象。

静态方法

Promise.all()

Promise.all(iterable) 方法接收一个promise的iterable类型的输入,并且只返回一个Promise实例,那个输入的所有promise的resolve回调的结果是一个数组。所有输入的promise的resolve的回调都结束了,才会返回这个方法的resolve。如果输入数组中有一个reject执行或者输入不合法的promise就会立马抛出错误,并且reject是第一个抛出的错误信息。

传入的参数为Array或者String类型。

  • 如果传入的参数是空的可迭代对象,直接同步返回已完成状态的Promise
  • 如果传入的参数不包含任何promise,返回一个异步完成的Promise
  • 其它情况返回pending的Promise,返回值上面说了。返回值将会按照参数内的 promise 顺序排列,而不是由调用 promise 的完成顺序决定。

同步和异步的展示,一般情况都是异步完成,当且仅当传入空的可迭代对象

var p = Promise.all([]); // 会被立马返回,同步的状态
var p2 = Promise.all([1337, "hi"]); // 非Promise的值会被略过,但是评估还是会异步完成
console.log(p);
console.log(p2)
setTimeout(function(){
    console.log('the stack is now empty'); // 使用setTimeout,在栈为空的时候才会被执行
    console.log(p2);
});

// 输出,空迭代对象立马返回
// Promise { <state>: "fulfilled", <value>: Array[0] }
// Promise { <state>: "pending" }
// the stack is now empty
// Promise { <state>: "fulfilled", <value>: Array[2] }

有一个有趣的现象,自己测试在这个例子里面,由于有reject,所以遇到reject会立马返回555,但是此时其他函数还在被执行,并没有被打断,经过了10秒之后,程序才正常关闭。也就是说all的确遇到reject会立马返回,但不会中断其他promise程序,让他们空跑,盲猜是这个是其他线程在跑(浏览器的线程或者nodejs的v8?),但是js管不到,只能让他们慢慢跑。

var p1 = Promise.resolve(3);
var p2 = Promise.reject(555);
var p3 = new Promise((resolve, reject) => {
    setTimeout(resolve, 10000, 'foo');
});
Promise.all([p1, p2, p3])
    .then((value) => console.log(value))
    .catch((err) => console.log(err));

Promise.allSettled()

Promise.allSettled()方法返回一个在所有给定的promise都已经fulfilledrejected后的promise,并带有一个对象数组,每个对象表示对应的promise结果。

Promise.allSettled()适合多个彼此不依赖的异步任务,想知道每个执行结果。

Promise.all()适合彼此相互依赖或者在任何一个reject的时候结束。

返回的对象有两个属性,不管是成功还是失败,都有status属性,如果值为fulfilled,那么另一个属性就是value‘’;如果值为rejected,那么另一个属性是reason

Promise.any()

这个方法挺新的,在chrome85或者nodejs15才正式支持。

和all()相反,只要有一个promise成功,就会返回那个成功的promise。如果没有一个成功,全部失败了,那么就会返回一个失败的promise和AggregateError类型的实例,用于把单一的错误集合在一起。

  • 传入一个空的可迭代对象,返回一个已失败
  • 不包含任何promise,返回一个异步完成的promise
  • 只要有一个成功或者全部失败,才会变状态。

Promise.race()

返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。

这个也挺好理解的,如果有多个settled的,那么就按迭代顺序返回第一个值。

Promise.resolve()

Promise.resolve(value);

value:将被Promise对象解析的参数,也可以是一个Promise对象,或者是一个thenable

Promise.resolve(value)方法返回一个以给定值解析后的Promise对象。如果这个值是一个 promise ,那么将返回这个 promise;如果这个值是thenable(即带有then方法),返回的promise会“跟随”这个thenable的对象,采用它的最终状态。

Promise.reject()

Promise.reject()方法返回一个带有拒绝原因的Promise对象。

实例方法

Promise.prototype.then()

then返回还是一个Promise对象,参数最多放两个,成功(可选)和失败(可选)的回调函数

p.then(onFulfilled[, onRejected]);

p.then(value => {
  // fulfillment
}, reason => {
  // rejection
});

onFulfilled函数有一个参数,即接受的最终结果。如果onFulfilled不是函数,那么会转换为(x)=>x,也就是直接返回promise的结果。

onRejected函数有一个参数,即拒绝的原因。如果onRejected不是函数,则会被替换为 Thrower 函数。

如果函数抛出错误或返回一个拒绝的Promise,则 then 将返回一个拒绝的Promise。(估计是当且仅当?)

Promise.resolve()
  .then(() => {
    // 使 .then() 返回一个 rejected promise
    throw new Error('Oh no!');
  })
  .then(() => {
    console.log('Not called.');
  }, error => {
    console.error('onRejected function called: ' + error.message);
  });

在其他情况下,一个 resolving Promise 会被返回。在下面的例子里,第一个 then() 会返回一个用 resolving Promise 包装的 42,即使之前的 Promise 是 rejected 的。

Promise.reject()
  .then(() => 99, () => 42) // onRejected returns 42 which is wrapped in a resolving Promise
  .then(solution => console.log('Resolved with ' + solution)); // Resolved with 42

如果 onFulfilled 返回了一个 promise,then 的返回值就会被 Promise resolved 或者 rejected。

Promise.prototype.catch()

catch() 方法返回一个Promise,并且处理拒绝的情况。它的行为与调Promise.prototype.then(undefined, onRejected) 相同。 (事实上, calling obj.catch(onRejected) 内部calls obj.then(undefined, onRejected)).

这个需要注意一下,在异步函数中抛出的错误不会被catch捕获到

var p2 = new Promise(function(resolve, reject) {
  setTimeout(function() {
    throw 'Uncaught Exception!';
  }, 1000);
});

p2.catch(function(e) {
  console.log(e); // 不会执行
});

Promise.prototype.finally()

finally() 方法返回一个Promise。在promise结束时,无论结果是fulfilled或者是rejected,都会执行指定的回调函数。这为在Promise是否成功完成后都需要执行的代码提供了一种方式。

这避免了同样的语句需要在then和catch中各写一次的情况。

p.finally(function() {
   // 返回状态为(resolved 或 rejected)
});

里面不需要传入参数,虽然这个叫finally,但是不代表这个没有返回值,也会返回一个Promise对象

  • 由于无法知道promise的最终状态,所以finally的回调函数中不接收任何参数,它仅用于无论最终结果如何都要执行的情况。
  • Promise.resolve(2).then(() => {}, () => {}) (resolved的结果为undefined)不同,Promise.resolve(2).finally(() => {}) resolved的结果为 2。(这个怪怪的)
  • 同样,Promise.reject(3).then(() => {}, () => {}) (fulfilled的结果为undefined), Promise.reject(3).finally(() => {}) rejected 的结果为 3

手写Promise

手写Promise比我想象中的难很多啊,我肯定还是不会写的。具体抄的是这篇文章

首先是需要定义状态,三个状态,pending、fulfilled、rejected三个状态,状态切换只能从pengding到其他两种状态。

然后需要实现resolve和reject方法,这个不是静态方法那个,而是函数内部的方法。resolve这里面需要考虑传进来的value参数是Promise类型的,需要等到value执行完成

然后就是要直接执行实例化里面的executor函数。

then是最麻烦的,主要要添加异步方法到队列里面,还要处理Promise的情况,递归嵌套,我也很难讲清楚。具体看代码吧。

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

function _Promise(executor) {
    const _this = this;
    this.status = PENDING; //状态
    this.result = undefined; //成功结果
    this.onFulfilled = []; //成功回调函数队列
    this.onRejected = []; //失败回调函数队列
    function resolve(value) {
        if (_this.status === PENDING) {
            // 如果是Promise类型的,那么就转换成异步的
            if (value instanceof _Promise) {
                value.then(
                    (v) => resolve(v),
                    (r) => reject(r)
                );
            } else {
                _this.status = FULFILLED;
                _this.result = value;
                _this.onFulfilled.forEach((fn) => fn(value));
            }
        }
    }
    function reject(reason) {
        if (_this.status === PENDING) {
            _this.status = REJECTED;
            _this.result = reason;
            _this.onRejected.forEach((fn) => fn(reason));
        }
    }
    try {
        executor(resolve, reject);
    } catch (error) {
        console.log('?');
        reject(error);
    }
}

_Promise.prototype.then = function (onFulfilled, onRejected) {
    const _this = this;
    // 检查是不是function,如果不是,就给个默认函数
    if (typeof onFulfilled !== 'function') onFulfilled = (value) => value;
    if (typeof onRejected !== 'function')
        onRejected = (reason) => {
            throw reason;
        };
    // 为了能够链式调用then,返回值一定要是_Promise的
    return new _Promise((resolve, reject) => {
        /**
         * 执行onFulfilled(value)和onRejected(reason),得到下一个结果
         * @param {onFulfilled|onRejected} callback
         * @param {any} result
         */
        function handle(callback, result) {
            try {
                const nextResult = callback(result);
                // 如果还是_Promise类型的
                if (nextResult instanceof _Promise) {
                    // 那么等nextRusult执行完成了,再把结果赋值给resolve
                    nextResult.then(
                        (v) => resolve(v),
                        (r) => reject(r)
                    );
                } else {
                    resolve(nextResult);
                }
            } catch (err) {
                reject(err);
            }
        }

        if (_this.status === FULFILLED) {
            /* 状态已经完成了,比如这种直接返回的情况
                Promise((resolve, reject) => {
                    resolve(3);
                }).then();
                虽然已经成功了,但是也不能直接主线程返回值,要通过异步的方式返回值(原生Promise就是这样的)
            */
            setTimeout(() => {
                handle(onFulfilled, _this.result);
            });
        } else if (_this.status === REJECTED) {
            setTimeout(() => {
                handle(onRejected, _this.result);
            });
        } else {
            // 主线程如果还在Pending,那么就先把回调函数放到队列里面
            // 等到主线程运行完,开始运行Promise里面的异步函数
            // 就会执行resolve/reject,从而调用队列里面的函数
            _this.onFulfilled.push(() => {
                handle(onFulfilled, _this.result);
            });
            _this.onRejected.push(() => {
                handle(onRejected, _this.result);
            });
        }
    });
};

// 实例方法catch
_Promise.prototype.catch = function (onRejected) {
    return this.then(undefined, onRejected);
};

// 实例方法finally
_Promise.prototype.finally = function (onFinally) {
    return this.then(onFinally, onFinally);
};

// 静态方法resolve
_Promise.resolve = function (value) {
    return new _Promise((resolve, reject) => {
        resolve(value);
    });
};

//静态方法resolve
_Promise.reject = function (reason) {
    return new _Promise((resolve, reject) => {
        reject(reason);
    });
};

测试用例,能够处理resove传进去是个Promise类型的例子,按序输出p1 p2

var p1 = new _Promise((resolve, reject) => {
    resolve('p1');
});
p1.then((res) => {
    console.log(res);
    return new _Promise((resolve, reject) => {
        resolve(
            new _Promise((resolve, reject) => {
                resolve('p2');
            })
        );
    });
}).then((res1) => {
    console.log(res1);
});

测试🌰2

console.log('开始');

const p = new _Promise((resolve, reject) => {
    console.log('输入');
    setTimeout(function () {
        resolve(1);
    }, 1000);
});
p.then(
    (value) => {
        console.log('1', value);
        return 2;
    },
    (err) => {
        console.log('err1', err);
    }
)
    .then((value) => {
        console.log('2', value);
        throw 'wrong!';
    })
    .catch((err) => {
        console.log('err2', err);
    });
    
console.log('结束');

输出

开始
输入
结束
1 1
2 2
err2 wrong!

蛋疼的Promise

我们在承诺方面有问题 (pouchdb.com)

面试题

1、手撸一个Promise.all()

  • [x] 非迭代对象处理(虽然不知道返回啥)
  • [x] 空的迭代对象直接同步返回,返回的对象还是Promise
  • [x] 如果迭代对象不是Promise,也需要异步处理
  • [x] 一旦有错误就返回错误信息
const PromiseAll = function (iterable) {
    // 如果是非迭代对象,直接返回
    const iterator = Symbol.iterator;
    if (!iterable[iterator]) return;
    const len = iterable.length;
    if (len == 0) return Promise.resolve(iterable);
    // 返回对象肯定是个Promise
    return new Promise((resolve, reject) => {
        // 结果数组
        let result = [];
        let cnt = 0;
        for (let [index, value] of iterable.entries()) {
            // 如果不是Promise对象,直接添加
            if (!(value instanceof Promise)) {
                value = Promise.resolve(value);
                /* 本来写成这样子的,但是发现Promise.all
                 * 传的不是promise数组,比如[1,2],也不是同步返回
                 * 反而还是在pending,执行过程依然是异步的
                 * 所以需要套娃一个Promise,变成异步的
                result[index] = value;
                cnt++;
                if (cnt == len) resolve(result);
                */
            }
            value
                .then((res) => {
                    result[index] = res;
                    cnt++;
                    if (cnt == len) resolve(result);
                })
                .catch((err) => reject(err));
        }
    });
};

2、trycatch

promise 中的错误能使用 try catch 捕获到吗,不能的话如何实现呢?

不行,try是同步函数的异常,不能用于捕捉异步函数的错误。不能的话就用await来实现。

3、看题说话1

const promise = new Promise((resolve, reject) => {
    console.log(1)
    resolve()
    console.log(2)
})
promise.then(() => {
    console.log(3)
})
console.log(4)

注意,promise的参数函数,是被同步执行的,所以输出是1 2 4 3

4、看题说话2

给出一个promise

var promise = new Promise(function(resolve, reject){
  setTimeout(function() {
    resolve(1);
  }, 3000)
})

三种情况有何不同

// 1
promise.then(() => {
  return Promise.resolve(2);
}).then((n) => {
  console.log(n)
});

// 2
promise.then(() => {
  return 2
}).then((n) => {
  console.log(n)
});

// 3
promise.then(2).then((n) => {
  console.log(n)
});
  1. 输出2。Promise.resolve 就是一个 Promise 对象就相当于返回了一个新的 Promise 对象。然后在下一个事件循环里才会去执行 then
  2. 输出2。和上一点不一样的是,它不用等下一个事件循环。
  3. 输出1。then 和 catch 期望接收函数做参数,如果非函数就会发生 Promise 穿透现象,打印的是上一个 Promise 的返回。

5、看题说话3

不是,怎么有这么变态的题目,真的恶心,直接复制网上的解析。

let a;
const b = new Promise((resolve, reject) => {
  console.log('promise1');
  resolve();
}).then(() => {
  console.log('promise2');
}).then(() => {
  console.log('promise3');
}).then(() => {
  console.log('promise4');
});

a = new Promise(async (resolve, reject) => {
  console.log(a);
  await b;
  console.log(a);
  console.log('after1');
  await a
  resolve(true);
  console.log('after2');
});

console.log('end');

第一个输出 promise1,是因为 Promise 里的方法立即执行。接着调用 resolve,只不过 then 里的方法等下一个周期

第二个输出 undefined,是因为立即执行执行 a 内部的方法,先 console.log(a),但此时的 a 还没赋值给左边的变量,所以只能是 undefined。然后 await b 就得等下一个周期执行了。

第三个输出 end,自然不意外。

接着输出 promise2,promise3,promise4,是因为 await b 等待他执行完了,才轮到 a 内部继续执行。

输出 Promise { pending },脑筋转了以下才想通,事件都进入了循环了,a 肯定已经被赋值成了 Promise 对象。所以第二遍 console.log(a),自然就输出这个了。

输出 after1 不奇怪。

但是随后的 await a 是个什么奇怪的操作,想半天没搞懂为何最后不输出 after2,调试得知根本就执行不到 await a 以后的代码上,想不懂。

更新:和不少朋友交流后,我得出了结论,await a 时,a 是必须等待 Promise 的状态从 pending 到 fullfilled 才会继续往下执行,可 a 的状态是一直得不到更改的,所以无法执行下面的逻辑。只要在 await a 上面加一行 resolve() 就能让后面的 after 2 得到输出。

6、看题说话4

这题我会,只会转移一次状态,所以输出success1

const promise = new Promise((resolve, reject) => {
  resolve('success1');
  reject('error');
  resolve('success2');
});

promise
  .then((res) => {
    console.log('then: ', res);
  })
  .catch((err) => {
    console.log('catch: ', err);
  });

7、看题说话5

这个我也会,会被then处理,因为没有throw错误,即便是Error对象

Promise.resolve()
  .then(() => {
    return new Error('error!!!')
  })
  .then((res) => {
    console.log('then: ', res)
  })
  .catch((err) => {
    console.log('catch: ', err)
  })

8、Promise的执行顺序1

new Promise((resolve) => {
    resolve();
    Promise.resolve().then(() => console.log(2));
}).then(() => console.log(4));

输出:2 4

里面的Promise.reslove()产生了一个promise1,并且立即settle,所以console.log(2)进入微队列。

外面的构造函数完成返回,产生一个promise2,promise立即settle,所以consol.log(4)进入微队列。

调用栈清空,开始依次执行微任务。

9、Promise的执行顺序2

new Promise((resolve) => {
    resolve();
    Promise.resolve({
        then: function (resolve, reject) {
            console.log(1);
            resolve();
        }
    }).then(() => console.log(2));
    console.log(0);
}).then(() => console.log(3));

当Promise.resolve()接收一个thenable参数时,即刻产生了一个promise1,并返回这个promise1,这个promise1被 settle的时机是thenable.then的resolve被调用时。

Promise.resolve({then:…})执行完毕后,将thenable.then放入到微任务里面,并返回一个promise1,然后执行console.log(0).

外面那个Promise代码块执行完后,调用栈为空,同时返回一个promise0。

然后promise0立刻settle,把console.log(3)放入到微队列。

然后执行thenable.then里面的console.log(1),resolve后,立刻settle,console.log(2)也放到微任务,然后依次3和2出列。

10、异步并发控制

如何实现一个异步并发控制,promiseConcurrencyLimit(限制并发量,数组,执行函数)

function get(i) {
    // console.log('In ', i);
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(i * 1000);
            console.log('Out', i, 'Out');
        }, 1000);
    });
}
const list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// 方法一:使用函数中的局部变量count来保证执行顺序,
function promiseConcurrencyLimit(limit, arr, fn) {
    let count = 0;
    function run() {
        if (count < arr.length) {
            fn(arr[count++]).then(() => {
                run();
            });
        }
    }
    for (let i = 0; i < limit; i++) run();
}

//	方法二:或者使用函数参量来保证,两个都扎不多
function promiseConcurrencyLimit(limit, arr, fn) {
    function run(count) {
        if (count < arr.length) {
            fn(arr[count]).then(() => {
                run(count + limit);
            });
        }
    }
    for (let i = 0; i < limit; i++) run(i);
}
promiseConcurrencyLimit(3, list, get);
// 执行顺序应该是,纵列表示时间是一致的(基本上),所以第一秒输出123,第二秒输出456,第三秒输出789,最后10
// 1 => 4 => 7 => 10
// 2 => 5 => 8
// 3 => 6 => 9
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇