承诺:执行某些事情而不管解决/拒绝?

nik*_*ohn 16 javascript asynchronous promise chain

使用Promises设计模式,是否可以实现以下功能:

 var a, promise

 if promise.resolve
     a = promise.responsevalue;

 if promise.reject
     a = "failed"

 AFTER resolution/rejection. Not ASYNC!!
     send a somewhere, but not asynchronously. //Not a promise
Run Code Online (Sandbox Code Playgroud)

我正在寻找的是类似finallytry - catch情况.

PS:我在NodeJS上使用ES6 Promise polyfill

T.J*_*der 15

如果从中返回值catch,则可以使用then结果catch.

thePromise.then(result => doSomething(result)
          .catch(error => handleErrorAndReturnSomething(error))
          .then(resultOrReturnFromCatch => /* ... */);
Run Code Online (Sandbox Code Playgroud)

......但这意味着你将拒绝转化为解决方案(通过返回某些内容catch而不是抛弃或返回被拒绝的承诺),并依赖于这一事实.


如果你想要一些透明地传递分辨率/拒绝而不修改它的东西,那么ES2015("ES6")承诺中没有任何内容可以做到这一点,但它很容易编写(这是在ES2015中,但我在下面有一个ES5翻译) :

{
    let worker = (p, f, done) => {
        return p.constructor.resolve(f()).then(done, done);
    };
    Object.defineProperty(Promise.prototype, "finally", {
        value(f) {
            return this.then(
                result => worker(this, f, () => result),
                error  => worker(this, f, () => { throw error; })
            );
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

例:

{
  let worker = (p, f, done) => {
    return p.constructor.resolve(f()).then(done, done);
  };
  Object.defineProperty(Promise.prototype, "finally", {
    value(f) {
      return this.then(
        result => worker(this, f, () => result),
        error  => worker(this, f, () => { throw error; })
      );
    }
  });
}
test("p1", Promise.resolve("good")).finally(
  () => {
    test("p2", Promise.reject("bad"));
  }
);
function test(name, p) {
  return p.then(
    result => {
      console.log(name, "initial resolution:", result);
      return result;
    },
    error => {
      console.log(name, "initial rejection; propagating it");
      throw error;
    }
  )
  .finally(() => {
    console.log(name, "in finally");
  })
  .then(
    result => {
      console.log(name, "resolved:", result);
    },
    error => {
      console.log(name, "rejected:", error);
    }
  );
}
Run Code Online (Sandbox Code Playgroud)

关于这一点的几点说明:

  1. 注意使用,this.constructor以便我们调用resolve任何类型的promise(包括可能的子类)创建原始的promise; 这Promise.resolve与其他人的工作方式一致,是支持子类承诺的重要组成部分.

  2. 以上是故意包括finally回调的任何参数,并且没有表明承诺是否被解决或拒绝,以便与finally经典try-catch-finally结构保持一致.但如果有人想要,可以轻松地将一些信息传递给回调.

  3. 同样的,上面没有使用由返回值finally的回调只是,如果它是一个承诺,它等待的承诺,允许链继续前解决.

这是ES5的翻译:

(function() {
    function worker(ctor, f, done) {
        return ctor.resolve(f()).then(done, done);
    }
    Object.defineProperty(Promise.prototype, "finally", {
        value: function(f) {
            var ctor = this.constructor;
            return this.then(
                function(result) {
                    return worker(ctor, f, function() {
                        return result;
                    });
                },
                function(error) {
                    return worker(ctor, f, function() {
                        throw error;
                    });
                }
            );
        }
    });
})();
Run Code Online (Sandbox Code Playgroud)

例:

(function() {
  function worker(ctor, f, done) {
    return ctor.resolve(f()).then(done, done);
  }
  Object.defineProperty(Promise.prototype, "finally", {
    value: function(f) {
      var ctor = this.constructor;
      return this.then(
        function(result) {
          return worker(ctor, f, function() {
            return result;
          });
        },
        function(error) {
          return worker(ctor, f, function() {
            throw error;
          });
        }
      );
    }
  });
})();

test("p1", Promise.resolve("good")).finally(function() {
  test("p2", Promise.reject("bad"));
});

function test(name, p) {
  return p.then(
      function(result) {
        console.log(name, "initial resolution:", result);
        return result;
      },
      function(error) {
        console.log(name, "initial rejection; propagating it");
        throw error;
      }
    )
    .finally(function() {
      console.log(name, "in finally");
    })
    .then(
      function(result) {
        console.log(name, "resolved:", result);
      },
      function(error) {
        console.log(name, "rejected:", error);
      }
    );
}
Run Code Online (Sandbox Code Playgroud)

我认为这是将此功能集成到ES5中的Promise polyfill中的最简单方法.


或者如果您更喜欢子类Promise而不是修改其原型:

let PromiseX = (() => {
    let worker = (p, f, done) => {
        return p.constructor.resolve(f()).then(done, done);
    };
    class PromiseX extends Promise {
        finally(f) {
            return this.then(
                result => worker(this, f, () => result),
                error  => worker(this, f, () => { throw error; })
            );
        }
    }
    PromiseX.resolve = Promise.resolve;
    PromiseX.reject = Promise.reject;

    return PromiseX;
})();
Run Code Online (Sandbox Code Playgroud)

例:

let PromiseX = (() => {
  let worker = (p, f, done) => {
    return p.constructor.resolve(f()).then(done, done);
  };
  class PromiseX extends Promise {
    finally(f) {
      return this.then(
        result => worker(this, f, () => result),
        error  => worker(this, f, () => { throw error; })
      );
    }
  }
  PromiseX.resolve = Promise.resolve;
  PromiseX.reject = Promise.reject;

  return PromiseX;
})();

test("p1", PromiseX.resolve("good")).finally(
  () => {
    test("p2", PromiseX.reject("bad"));
  }
);
function test(name, p) {
  return p.then(
    result => {
      console.log(name, "initial resolution:", result);
      return result;
    },
    error => {
      console.log(name, "initial rejection; propagating it");
      throw error;
    }
  )
  .finally(() => {
    console.log(name, "in finally");
  })
  .then(
    result => {
      console.log(name, "resolved:", result);
    },
    error => {
      console.log(name, "rejected:", error);
    }
  );
}
Run Code Online (Sandbox Code Playgroud)


你已经说过要在没有扩展Promise.prototype 子类化的情况下做到这一点.在ES5中,实用程序功能使用起来非常笨拙,因为你必须将它作为承诺传递给它,这与正常的承诺使用完全不同步.在ES2015中,可以做一些更自然的事情,但是调用还是比修改原型或子类更难:

let always = (() => {
    let worker = (f, done) => {
        return Promise.resolve(f()).then(done, done);
    };
    return function always(f) {
        return [
            result => worker(f, () => result),
            error  => worker(f, () => { throw error; })
        ];
    }
})();
Run Code Online (Sandbox Code Playgroud)

用法:

thePromise.then(...always(/*..your function..*/)).
Run Code Online (Sandbox Code Playgroud)

注意使用扩展运算符(这就是为什么这在ES5中不起作用),因此always可以提供两个参数then.

例:

let always = (() => {
  let worker = (f, done) => {
    return Promise.resolve(f()).then(done, done);
  };
  return function always(f) {
    return [
      result => worker(f, () => result),
      error  => worker(f, () => { throw error; })
    ];
  }
})();

test("p1", Promise.resolve("good")).then(...always(
  () => {
    test("p2", Promise.reject("bad"));
  }
));
function test(name, p) {
  return p.then(
    result => {
      console.log(name, "initial resolution:", result);
      return result;
    },
    error => {
      console.log(name, "initial rejection; propagating it");
      throw error;
    }
  )
  .then(...always(() => {
    console.log(name, "in finally");
  }))
  .then(
    result => {
      console.log(name, "resolved:", result);
    },
    error => {
      console.log(name, "rejected:", error);
    }
  );
}
Run Code Online (Sandbox Code Playgroud)


在评论中,你表示担心finally不会等待承诺; 这是最后一个always例子,延迟证明它确实:

let always = (() => {
  let worker = (f, done) => {
    return Promise.resolve(f()).then(done, done);
  };
  return function always(f) {
    return [
      result => worker(f, () => result),
      error  => worker(f, () => { throw error; })
    ];
  }
})();

test("p1", 500, false, "good").then(...always(
  () => {
    test("p2", 500, true, "bad");
  }
));

function test(name, delay, fail, value) {
  // Make our test promise
  let p = new Promise((resolve, reject) => {
    console.log(name, `created with ${delay}ms delay before settling`);
    setTimeout(() => {
      if (fail) {
        console.log(name, "rejecting");
        reject(value);
      } else {
        console.log(name, "resolving");
        resolve(value);
      }
    }, delay);
  });

  // Use it
  return p.then(
    result => {
      console.log(name, "initial resolution:", result);
      return result;
    },
    error => {
      console.log(name, "initial rejection; propagating it");
      throw error;
    }
  )
  .then(...always(() => {
    console.log(name, "in finally");
  }))
  .then(
    result => {
      console.log(name, "resolved:", result);
    },
    error => {
      console.log(name, "rejected:", error);
    }
  );
}
Run Code Online (Sandbox Code Playgroud)