• 首页
  • vue
  • TypeScript
  • JavaScript
  • scss
  • css3
  • html5
  • php
  • MySQL
  • redis
  • jQuery
  • Promise.prototype.then()

    then()方法返回一个Promise。它最多需要有两个参数:Promise 的成功和失败情况的回调函数。

    注意:如果忽略针对某个状态的回调函数参数,或者提供非函数(nonfunction)参数,那么then方法将会丢失关于该状态的回调函数信息,但是并不会产生错误。如果调用thenPromise的状态(fulfillment 或 rejection)发生改变,但是then中并没有关于这种状态的回调函数,那么then将创建一个没有经过回调函数处理的新Promise对象,这个新Promise只是简单地接受调用这个then的原Promise的终态作为它的终态。

    语法

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

    参数

    onFulfilled可选
    当 Promise 变成接受状态(fulfilled)时调用的函数。该函数有一个参数,即接受的最终结果(the fulfillment value)。如果该参数不是函数,则会在内部被替换为(x)=> x,即原样返回 promise 最终结果的函数
    onRejected可选
    当 Promise 变成接受状态或拒绝状态(rejected)时调用的函数。该函数有一个参数,即拒绝的原因(rejection reason)。如果该参数不是函数,则会在内部被替换为一个"Thrower"函数(it throws an error it received as argument)。

    返回值

    当一个Promise完成(fulfilled)或者失败(rejected)时,返回函数将被异步调用(由当前的线程循环来调度完成)。具体的返回值依据以下规则返回。如果then中的回调函数:

    • 返回了一个值,那么then返回的 Promise 将会成为接受状态,并且将返回的值作为接受状态的回调函数的参数值。
    • 没有返回任何值,那么then返回的 Promise 将会成为接受状态,并且该接受状态的回调函数的参数值为undefined
    • 抛出一个错误,那么then返回的 Promise 将会成为拒绝状态,并且将抛出的错误作为拒绝状态的回调函数的参数值。
    • 返回一个已经是接受状态的 Promise,那么then返回的 Promise 也会成为接受状态,并且将那个 Promise 的接受状态的回调函数的参数值作为该被返回的Promise的接受状态回调函数的参数值。
    • 返回一个已经是拒绝状态的 Promise,那么then返回的 Promise 也会成为拒绝状态,并且将那个 Promise 的拒绝状态的回调函数的参数值作为该被返回的Promise的拒绝状态回调函数的参数值。
    • 返回一个未定状态(pending)的 Promise,那么then返回 Promise 的状态也是未定的,并且它的终态与那个 Promise 的终态相同;同时,它变为终态时调用的回调函数参数与那个 Promise 变为终态时的回调函数的参数是相同的。

    下面是一个演示 then方法的同步性的例子。

    // using a resolved promise, the 'then' block will be triggered instantly, 
    // but its handlers will be triggered asynchronously as demonstrated by the console.logs
    const resolvedProm = Promise.resolve(33);
    
    let thenProm = resolvedProm.then(value => {
        console.log("this gets called after the end of the main stack. the value received and returned is: " + value);
        return value;
    });
    // instantly logging the value of thenProm
    console.log(thenProm);
    
    // using setTimeout we can postpone the execution of a function to the moment the stack is empty
    setTimeout(() => {
        console.log(thenProm);
    });
    
    // 上面的代码会依次返回:
    // Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined}
    // "this gets called after the end of the main stack. the value received and returned is: 33"
    // Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: 33}
    

    描述

    由于thenPromise.prototype.catch()方法都会返回 promise,它们可以被链式调用——这同时也是一种被称为复合composition)的操作。

    示例

    使用then方法

    var p1 = new Promise((resolve, reject) => {
      resolve('成功!');
      // or
      // reject(new Error("出错了!"));
    });
    
    p1.then(value => {
      console.log(value); // 成功!
    }, reason => {
      console.error(reason); // 出错了!
    });
    

    链式调用

    then方法返回一个Promise对象,其允许方法链。

    你可以传递一个匿名函数给then,并且,如果它返回一个Promise,一个等价的Promise将暴露给后续的方法链。下面的代码片段使用setTimout函数来模拟异步代码操作。

    Promise.resolve("foo")
      // 1. 接收 "foo" 并与 "bar" 拼接,并将其结果做为下一个 resolve 返回。
      .then(function(string) {
        return new Promise(function(resolve, reject) {
          setTimeout(function() {
            string += 'bar';
            resolve(string);
          }, 1);
        });
      })
      // 2. 接收 "foobar", 放入一个异步函数中处理该字符串
      // 并将其打印到控制台中, 但是不将处理后的字符串返回到下一个。
      .then(function(string) {
        setTimeout(function() {
          string += 'baz';
          console.log(string);
        }, 1)
        return string;
      })
      // 3. 打印本节中代码将如何运行的帮助消息,
      // 字符串实际上是由上一个回调函数之前的那块异步代码处理的。
      .then(function(string) {
        console.log("Last Then:  oops... didn't bother to instantiate and return " +
                    "a promise in the prior then so the sequence may be a bit " +
                    "surprising");
    
        // 注意 `string` 这时不会存在 'baz'。
        // 因为这是发生在我们通过setTimeout模拟的异步函数中。
        console.log(string);
      });
    
    // logs, in order:
    // Last Then: oops... didn't bother to instantiate and return a promise in the prior then so the sequence may be a bit surprising
    // foobar
    // foobarbaz
    

    当一个值只是从一个then内部返回时,它将等价地返回Promise.resolve(<由被调用的处理程序返回的值>)

    var p2 = new Promise(function(resolve, reject) {
      resolve(1);
    });
    
    p2.then(function(value) {
      console.log(value); // 1
      return value + 1;
    }).then(function(value) {
      console.log(value + ' - A synchronous value works');
    });
    
    p2.then(function(value) {
      console.log(value); // 1
    });
    

    如果函数抛出错误或返回一个拒绝的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
    

    实际上,捕获 rejected promise 的需求经常大于使用then的两种情况语法,比如下面这样的:

    Promise.resolve()
      .then(() => {
        // 使 .then() 返回一个 rejected promise
        throw new Error('Oh no!');
      })
      .catch(error => {
        console.error('onRejected function called: ' + error.message);
      })
      .then(() => {
        console.log("I am always called even if the prior then's promise rejects");
      });
    

    你也可以在另一个顶层函数上使用链式去实现基于 Promise API 的函数。

    function fetch_current_data() {
      // fetch() API 返回了一个 Promise.
      // 这个函数提供了类似的API,
      // 这个函数除了实现 Promise,它还能够完成更多的工作。
      return fetch('current-data.json').then(response => {
        if (response.headers.get('content-type') != 'application/json') {
          throw new TypeError();
        }
        var j = response.json();
        // maybe do something with j
        return j; // fulfillment value given to user of
                  // fetch_current_data().then()
      });
    }
    

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

    function resolveLater(resolve, reject) {
      setTimeout(function() {
        resolve(10);
      }, 1000);
    }
    function rejectLater(resolve, reject) {
      setTimeout(function() {
        reject(new Error('Error'));
      }, 1000);
    }
    
    var p1 = Promise.resolve('foo');
    var p2 = p1.then(function() {
      // Return promise here, that will be resolved to 10 after 1 second
      return new Promise(resolveLater);
    });
    p2.then(function(v) {
      console.log('resolved', v);  // "resolved", 10
    }, function(e) {
      // not called
      console.error('rejected', e);
    });
    
    var p3 = p1.then(function() {
      // Return promise here, that will be rejected with 'Error' after 1 second
      return new Promise(rejectLater);
    });
    p3.then(function(v) {
      // not called
      console.log('resolved', v);
    }, function(e) {
      console.error('rejected', e); // "rejected", 'Error'
    });
    

    基于 promise 的window.setImmediate polyfill

    Using a Reflect.apply())method to create a(non-cancellable)setImmediate-style function.

    const nextTick = (() => {
      const noop = () => {}; // literally
      const nextTickPromise = () => Promise.resolve().then(noop);
    
      const rfab = Reflect.apply.bind; // (thisArg, fn, thisArg, [...args])
      const nextTick = (fn, ...args) => (
        fn !== undefined
        ? Promise.resolve(args).then(rfab(null, fn, null))
        : nextTickPromise(),
        undefined
      );
      nextTick.ntp = nextTickPromise;
    
      return nextTick;
    })();