关于程序员:快速了解-ES6-的Promise

35次阅读

共计 6794 个字符,预计需要花费 17 分钟才能阅读完成。

ECMAScript 6 减少了对 Promises/A+ 标准的欠缺反对,即 Promise 类型。一经推出,Promise 就大受欢迎,成为了主导性的异步编程机制。所有古代浏览器都反对 ES6 期约,很多其余浏览器 API 也以期约为根底。

Promise 是 ECMAScript 6 新增的援用类型,示意一个异步操作的最终实现或者失败。Promise 创建对象时的语法如下:

let promise = new Promise(function(resolve, reject) => {
    // 异步解决
    //   resolve(value)              // fulfilled
    // or
    //   reject(error)    // rejected
});

由以上可知,Promise 是一个有状态的对象,返回的 Promise 对象可能处于以下几种状态之一:

  • 待定(pending):初始状态,即没有被兑现,也没有被回绝。
  • 兑现(fulfilled,有时也称为“解决”,resolved):意味着操作胜利实现。
  • 已回绝(rejected):意味着操作失败。

待定(pending)是 Promise 的最初始状态。在待定状态下,Promise 能够落定(settled)为代表胜利的兑现(fulfilled)状态,或者代表失败的回绝(rejected)状态。无论落定为哪种状态都是不可逆的。只有从待定转换为兑现或回绝,Promise 的状态就不再扭转。而且,也不能保障 Promise 必然会脱离待定状态。因而,组织正当的代码无论 Promise 解决(resolve)还是回绝(reject),甚至永远处于待定(pending)状态,都应该具备失当的行为。

重要的是,Promise 的状态是公有的,不能间接通过 JavaScript 检测到。这次要是为了防止依据读取到的 Promise 的状态,以同步形式解决 Promise 实例。另外,Promise 的状态也不能被内部 JavaScript 代码批改。这与不能读取该状态的起因是一样的:Promise 成心将异步行为封装起来,从而隔离内部的同步代码。

Promise 实例办法

Promise 实例的办法是连贯内部同步代码与外部异步代码之间的桥梁。这些办法能够拜访异步操作返回的数据,解决 Promise 对象胜利和失败的后果,间断对 Promise 求值,或者增加只有 Promise 进入终止状态时才会执行的代码。

Promise.prototype.then()

Promise.prototype.then() 办法返回一个 Promise 实例,是为 Promise 实例增加解决的次要办法。

Promise.prototype.then(onFulfilled, onRejected)
  • onFulfilled:可选参数。当 Promise 状态变为 fulfilled 时调用的函数。传入的任何非函数类型都会被静默疏忽。
  • onRejected:可选参数。当 Promise 状态变为 rejected 时调用的函数。传入的任何非函数类型都会被静默疏忽。

当值提供 onRejected 参数时,须要在 onFulfilled 参数地位上传 undefined。有助于防止在内存中创立多余的对象,对期待函数参数的类型零碎也是一个交代。

Promise.prototype.catch()

Promise.prototype.catch() 办法返回一个 Promise 实例,并且解决回绝的状况。

Promise.prototype.catch(onRejected)
  • onRejected:当 Promiserejected 时,被调用的一个 Function。该函数领有一个参数 reason,示意 rejection 的起因。

如果 onRejected 抛出一个谬误或返回一个自身失败的 Promise,通过 catch() 返回的 Promiserejected;否则,它将显示为胜利(resolved)。该办法能够用于 promise 组合中的错误处理。事实上,这个办法就是一个语法糖,调用它就相当于 Promise.prototype.then(null, onRejected)

Promise.prototype.finally()

Promise.prototype.finally() 办法返回一个 Promise 实例,并且在 Promise 解决完结时,无论什么状态,都会执行指定的回调函数。这为在 Promise 是否胜利实现后都须要执行的代码提供了一种形式。也防止了在 then()catch() 中呈现冗余代码。

Promise.prototype.finally(onFinally)
  • onFinally:当 Promise 接管后调用的 Function

然而上述的 onFinally 函数中无奈晓得 Promise 是处于什么状态,所以这个办法次要用于增加清理代码。

错误处理

状态为 rejectedPromise 相似于 throw() 表达式,都代表一种程序状态,即须要中断或者非凡解决。Promise 执行期间抛出谬误会导致 rejected,对应的 Error 会成为回绝的理由。

let p = new Promise((resolve, reject) => reject(throw Error('Error Value')));

Promise 实例中抛出谬误会从音讯队列中异步抛出,并不会阻止运行时继续执行同步指令:

Promise.reject(Error('Error Value'));
console.log('initial value');
// initial value
// Uncaught (in promise) Error: Error Value

因而,Promise 只能通过 catch 实例办法捕捉谬误,try-catch 语句块不好使。Promisethencatch 办法就相当于 try-catch 了。

Promise 静态方法

Promise 援用类型中存在多个静态方法,除了 Promise.reject()Promise.resolve() 外,都会接管一个 Promiseiterable 类型的参数,合成的 Promise 行为取决于外部 Promise 的行为。

留神:Promise.any() 办法还是实验性,尚未齐全反对。

Promise.reject()

Promise.reject() 会返回一个状态为 rejectedPromise 实例并抛出一个异步谬误。

Promise.reject(reason);
  • reason:示意 Promise 实例的失败信息。

应用 Promise.reject()new Promise((resolve, reject) => reject()) 相比,实际上是一样的。而在传入谬误起因时,能够传入一个 Error 实例,使它成为返回的 Promise 实例的谬误理由:

Promise.reject(new Error("Promise instance failed")).then(function() {// not called}, function(error) {console.error(error);    // Stacktrace
});
setTimeout(console.log, 0, Promise.reject(Promise.resolve()));
// Promise <rejected>: Promise <resolved>

Promise.resolve()

通过 Promise.resolve() 静态方法,能够实例化一个状态为 fulfilledPromise 实例。与 new Promise((resolve, reject) => resolve()) 实际上是一样的。

Promise.resolve(value)
  • value:将被 Promise 实例对象解析的参数,也能够是一个 Promise 实例,或者是一个 thenable

应用这个静态方法,能够把任何值都转换为状态是 fulfilledPromise 的实例,而传入多余的值会被疏忽。

setTimeout(console.log, 0, Promise.resolve());
// Promise <resolved>: undefined
setTimeout(console.log, 0, Promise.resolve(4, 5, 6));
// Promise <resolved>: 4

当传入的参数自身是一个 Promise 实例时,该行为相似于一个空包装。因而,Promise.resolve() 能够说是一个幂等办法。

let p = Promise.resolve(7);
setTimeout(console.log, 0, p === Promise.resolve(p));
// true
setTimeout(console.log, 0, p === Promise.resolve(Promise.resolve(p)));
// true

这个幂等性会保留传入期约的状态:

let p = new Promise(() => {});
setTimeout(console.log, 0, p); // Promise <pending>
setTimeout(console.log, 0, Promise.resolve(p)); // Promise <pending>
setTimeout(console.log, 0, p === Promise.resolve(p)); // true

然而,自身可能包装任何非 Promise 值,包含谬误对象,并将其转为解决的 Promise 实例,这会导致不合乎预期的行为,这点要留神:

let p = Promise.resolve(new Error('foo'));
setTimeout(console.log, 0, p);
// Promise <resolved>: Error: foo

Promise.all()

Promise.all() 办法接管具备 Promise 实例的 iterable 类型的汇合,返回一个新 Promise 实例。

当所有的 Promise 实例状态都变为 fulfilled,返回的 Promise 实例的状态变为 fulfilled,而且解决值就是所有蕴含 Promise 实例解决值的数组,依照迭代器程序。

let p = Promise.all([Promise.resolve(3),
    Promise.resolve(),
    Promise.resolve(4)
]);
p.then((values) => setTimeout(console.log, 0, values)); // [3, undefined, 4]

当有一个 Promise 实例的状态变为 rejected,返回的 Promise 实例的状态变为 rejected,且返回的回绝理由为状态变为 rejectedPromise 实例的回绝理由。之后的 Promise 实例再回绝不会影响最终 Promise 的回绝理由。不过,这并不影响所有蕴含 Promise 实例失常的回绝操作。Promise.all 返回的 Promise 实例会静默解决所有蕴含 Promise 实例的回绝操作,如下所示:

// 尽管只有第一个期约的回绝理由会进入
// 回绝处理程序,第二个期约的回绝也
// 会被静默解决,不会有谬误跑掉
let p = Promise.all([Promise.reject(3),
    new Promise((resolve, reject) => setTimeout(reject, 1000))
]);
p.catch((reason) => setTimeout(console.log, 0, reason)); // 3
// 没有未解决的谬误 

Promise.allSettled()

ECMAScript 2020 新增了 Promise.allSettled() 办法。Promise.allSettled() 办法接管的 iterable 汇合中,Promise 实例的状态都为 fulfilledrejected

Promise.allSettled([Promise.resolve(3),
    new Promise((resolve, reject) => {setTimeout(reject, 1000);
    }),
    Promise.reject('reject promise')
]).then((results) => console.log(results));

/**
 * 输入后果
 * [*   { status: 'fulfilled', value: 3},
 *   {status: 'rejected', reason: undefined},
 *   {status: 'rejected', reason: 'reject promise'}
 * ]
 */

当想要确保所有 Promise 实例都完结时,Promise.allSettled() 办法就很有用。

Promise.race()

Promise.race() 办法接管的 iterable 汇合中,只有有一个 Promise 实例的状态变为 fulfilled 或者 rejected,就会包装其解决值或回绝理由并返回新 Promise 实例。

// 解决先产生,超时后的回绝被疏忽
let p1 = Promise.race([Promise.resolve(3),
    new Promise((resolve, reject) => setTimeout(reject, 1000))
]);
setTimeout(console.log, 0, p1); // Promise <resolved>: 3
// 回绝先产生,超时后的解决被疏忽
let p2 = Promise.race([Promise.reject(4),
    new Promise((resolve, reject) => setTimeout(resolve, 1000))
]);
setTimeout(console.log, 0, p2); // Promise <rejected>: 4

Promise.all() 相似,回绝的 Promise 实例中的回绝理由会成为 Promise.race() 的回绝理由,iterable 汇合中余下的 Promise 实例会被静默解决,不会有谬误跑掉。

// 尽管只有第一个期约的回绝理由会进入,回绝处理程序,第二个期约的回绝也会被静默解决,不会有谬误跑掉
let p = Promise.race([Promise.reject(3),
    new Promise((resolve, reject) => setTimeout(reject, 1000))
]);
p.catch((reason) => setTimeout(console.log, 0, reason)); // 3
// 没有未解决的谬误 

链式调用

Promise 提供的 then()catch()finally() 办法都会返回一个新的 Promise 实例,新的 Promise 实例又能够调用这些办法,能够连缀办法调用形成链式调用。如下所示:

let p = new Promise((resolve, reject) => {console.log('initial promise');
    reject();}).catch(() => {console.log('reject handler');
}).then(() => {console.log('resolve handler');
}).finally(() => {console.log('finally handler');
});

/**
 * 输入后果
 * initial promise
 * reject handler
 * resolve handler
 * finally handler
 */

链式调用会依照程序执行,每个 Promise 都会期待前一个 Promise 解决,而后实例化一个新的 Promise 实例返回它,这样的构造能够简洁地将异步工作串行化。

let p = new Promise((resolve, reject) => {console.log('initial promise');
    setTimeout(resolve, 1000);
}).then(() => new Promise((resolve, reject) => {console.log('first promise');
    setTimeout(resolve, 3000);
})).then(() => new Promise((resolve, reject) => {console.log("second promise");
    setTimeout(resolve, 2000);
})).then(() => new Promise((resolve, reject) => {console.log("third promise");
    setTimeout(resolve, 1000);
}));
/**
 * 输入后果
 * initial promise
 * first promise
 * second promise
 * third promise
 */

更多内容请关注公众号「 海人为记

正文完
 0