Membatalkan rantai janji ECMAScript 6 vanilla

110

Apakah ada metode untuk menghapus instance .thenJavaScript Promise?

Saya telah menulis kerangka pengujian JavaScript di atas QUnit . Kerangka kerja menjalankan pengujian secara sinkron dengan menjalankan masing-masing pengujian di a Promise. (Maaf untuk panjang blok kode ini. Saya berkomentar sebaik mungkin, jadi rasanya tidak terlalu membosankan.)

/* Promise extension -- used for easily making an async step with a
       timeout without the Promise knowing anything about the function 
       it's waiting on */
$$.extend(Promise, {
    asyncTimeout: function (timeToLive, errorMessage) {
        var error = new Error(errorMessage || "Operation timed out.");
        var res, // resolve()
            rej, // reject()
            t,   // timeout instance
            rst, // reset timeout function
            p,   // the promise instance
            at;  // the returned asyncTimeout instance

        function createTimeout(reject, tempTtl) {
            return setTimeout(function () {
                // triggers a timeout event on the asyncTimeout object so that,
                // if we want, we can do stuff outside of a .catch() block
                // (may not be needed?)
                $$(at).trigger("timeout");

                reject(error);
            }, tempTtl || timeToLive);
        }

        p = new Promise(function (resolve, reject) {
            if (timeToLive != -1) {
                t = createTimeout(reject);

                // reset function -- allows a one-time timeout different
                //    from the one original specified
                rst = function (tempTtl) {
                    clearTimeout(t);
                    t = createTimeout(reject, tempTtl);
                }
            } else {
                // timeToLive = -1 -- allow this promise to run indefinitely
                // used while debugging
                t = 0;
                rst = function () { return; };
            }

            res = function () {
                clearTimeout(t);
                resolve();
            };

            rej = reject;
        });

        return at = {
            promise: p,
            resolve: res,
            reject: rej,
            reset: rst,
            timeout: t
        };
    }
});

/* framework module members... */

test: function (name, fn, options) {
    var mod = this; // local reference to framework module since promises
                    // run code under the window object

    var defaultOptions = {
        // default max running time is 5 seconds
        timeout: 5000
    }

    options = $$.extend({}, defaultOptions, options);

    // remove timeout when debugging is enabled
    options.timeout = mod.debugging ? -1 : options.timeout;

    // call to QUnit.test()
    test(name, function (assert) {
        // tell QUnit this is an async test so it doesn't run other tests
        // until done() is called
        var done = assert.async();
        return new Promise(function (resolve, reject) {
            console.log("Beginning: " + name);

            var at = Promise.asyncTimeout(options.timeout, "Test timed out.");
            $$(at).one("timeout", function () {
                // assert.fail() is just an extension I made that literally calls
                // assert.ok(false, msg);
                assert.fail("Test timed out");
            });

            // run test function
            var result = fn.call(mod, assert, at.reset);

            // if the test returns a Promise, resolve it before resolving the test promise
            if (result && result.constructor === Promise) {
                // catch unhandled errors thrown by the test so future tests will run
                result.catch(function (error) {
                    var msg = "Unhandled error occurred."
                    if (error) {
                        msg = error.message + "\n" + error.stack;
                    }

                    assert.fail(msg);
                }).then(function () {
                    // resolve the timeout Promise
                    at.resolve();
                    resolve();
                });
            } else {
                // if test does not return a Promise, simply clear the timeout
                // and resolve our test Promise
                at.resolve();
                resolve();
            }
        }).then(function () {
            // tell QUnit that the test is over so that it can clean up and start the next test
            done();
            console.log("Ending: " + name);
        });
    });
}

Jika tes habis waktu, Janji batas waktu saya akan assert.fail()di tes sehingga tes ditandai sebagai gagal, yang semuanya baik dan bagus, tetapi tes terus berjalan karena tes Promise ( result) masih menunggu untuk menyelesaikannya.

Saya perlu cara yang baik untuk membatalkan tes saya. Saya dapat melakukannya dengan membuat bidang pada modul kerangka kerja this.cancelTestatau sesuatu, dan memeriksa sesering mungkin (misalnya pada awal setiap then()iterasi) dalam pengujian apakah akan membatalkan. Namun, idealnya, saya dapat menggunakan $$(at).on("timeout", /* something here */)untuk menghapus sisa then()s pada resultvariabel saya , sehingga tidak ada sisa pengujian yang dijalankan.

Apakah ada yang seperti ini?

Pembaruan Cepat

Saya mencoba menggunakan Promise.race([result, at.promise]). Tidak berhasil.

Perbarui 2 + kebingungan

Untuk membebaskan saya, saya menambahkan beberapa baris dengan mod.cancelTest/ polling di dalam ide pengujian. (Saya juga menghapus pemicu acara.)

return new Promise(function (resolve, reject) {
    console.log("Beginning: " + name);

    var at = Promise.asyncTimeout(options.timeout, "Test timed out.");
    at.promise.catch(function () {
        // end the test if it times out
        mod.cancelTest = true;
        assert.fail("Test timed out");
        resolve();
    });

    // ...
    
}).then(function () {
    // tell QUnit that the test is over so that it can clean up and start the next test
    done();
    console.log("Ending: " + name);
});

Saya menetapkan breakpoint dalam catchpernyataan tersebut, dan itu dipukul. Yang membingungkan saya sekarang adalah then()pernyataan itu tidak dipanggil. Ide ide?

Perbarui 3

Pikir hal terakhir. fn.call()melakukan kesalahan yang tidak saya tangkap, jadi janji pengujian ditolak sebelum at.promise.catch()bisa menyelesaikannya.

dx_over_dt
sumber
Dimungkinkan untuk melakukan pembatalan dengan promise ES6 tetapi itu bukan properti dari promise (sebaliknya - ini adalah properti dari fungsi yang mengembalikannya) Saya dapat membuat contoh singkat jika Anda tertarik.
Benjamin Gruenbaum
@BenjaminGruenbaum Saya tahu sudah hampir setahun, tetapi saya masih tertarik jika Anda punya waktu untuk menulis contoh. :)
dx_over_dt
1
Sudah setahun yang lalu tetapi telah secara resmi dibahas dua hari sebelum kemarin dengan token pembatalan dan janji yang dapat dibatalkan pindah ke tahap 1.
Benjamin Gruenbaum
3
Jawaban ES6 untuk membatalkan Janji bisa diamati. Anda dapat membaca lebih lanjut tentang ini di sini: github.com/Reactive-Extensions/RxJS
Frank Goortani
Menghubungkan jawaban saya tentang menggunakan Prexperpustakaan untuk pembatalan janji.
noseratio

Jawaban:

75

Apakah ada metode untuk menghapus .thencontoh JavaScript Promise?

Tidak. Setidaknya tidak di ECMAScript 6. Promises (dan thenpenangannya) tidak dapat dibatalkan secara default (sayangnya) . Ada sedikit diskusi tentang es-diskusikan (misalnya di sini ) tentang bagaimana melakukan ini dengan cara yang benar, tetapi pendekatan apa pun yang akan menang, itu tidak akan masuk ke ES6.

Sudut pandang saat ini adalah bahwa subclassing akan memungkinkan untuk membuat janji yang dapat dibatalkan menggunakan implementasi Anda sendiri (tidak yakin seberapa baik itu akan berhasil) .

Sampai komite bahasa menemukan cara terbaik (semoga ES7?) Anda masih dapat menggunakan implementasi Promise userland, banyak di antaranya pembatalan fitur.

Diskusi saat ini ada di https://github.com/domenic/cancelable-promise dan draf https://github.com/bergus/promise-cancellation .

Bergi
sumber
2
"Sedikit diskusi" - Saya dapat menautkan ke sekitar 30 utas di esdiscuss atau GitHub :) (belum lagi bantuan Anda sendiri untuk pembatalan di bluebird 3.0)
Benjamin Gruenbaum
@BenjaminGruenbaum: Apakah Anda memiliki tautan yang siap untuk dibagikan di suatu tempat? Saya sudah lama ingin meringkas opini dan upaya dan memposting proposal untuk dibahas, jadi saya akan senang jika saya dapat memeriksa kembali bahwa saya tidak lupa apa-apa.
Bergi
Saya memiliki mereka berguna di tempat kerja - jadi saya akan memilikinya dalam 3-4 hari. Anda dapat memeriksa spesifikasi pembatalan janji di bawah promise-aplus untuk awal yang baik.
Benjamin Gruenbaum
1
@ LUH3417: Fungsi "normal" hanya membosankan dalam hal itu. Anda memulai program dan menunggu sampai selesai - atau Anda killmengabaikannya dan mengabaikan dalam keadaan yang mungkin aneh efek samping telah meninggalkan lingkungan Anda (jadi Anda biasanya membuangnya juga, misalnya keluaran setengah jadi). Namun, fungsi non-pemblokiran atau asinkron dibuat untuk bekerja dalam aplikasi interaktif, di mana Anda ingin memiliki kontrol yang lebih baik atas pelaksanaan operasi yang sedang berjalan.
Bergi
6
Domenic menghapus proposal TC39 ... ... cc @BenjaminGruenbaum
Sergio
50

Meskipun tidak ada cara standar untuk melakukan ini di ES6, ada pustaka bernama Bluebird untuk menangani ini.

Ada juga cara yang direkomendasikan yang dijelaskan sebagai bagian dari dokumentasi react. Ini terlihat mirip dengan apa yang Anda miliki di pembaruan ke-2 dan ke-3.

const makeCancelable = (promise) => {
  let hasCanceled_ = false;

  const wrappedPromise = new Promise((resolve, reject) => {
    promise.then((val) =>
      hasCanceled_ ? reject({isCanceled: true}) : resolve(val)
    );
    promise.catch((error) =>
      hasCanceled_ ? reject({isCanceled: true}) : reject(error)
    );
  });

  return {
    promise: wrappedPromise,
    cancel() {
      hasCanceled_ = true;
    },
  };
};

const cancelablePromise = makeCancelable(
  new Promise(r => component.setState({...}}))
);

cancelablePromise
  .promise
  .then(() => console.log('resolved'))
  .catch((reason) => console.log('isCanceled', reason.isCanceled));

cancelablePromise.cancel(); // Cancel the promise

Diambil dari: https://facebook.github.io/react/blog/2015/12/16/ismounted-antipattern.html

Michael Yagudaev
sumber
1
Definisi batal ini hanya menolak janji. itu tergantung pada definisi "dibatalkan".
Alexander Mills
1
Dan apa yang terjadi jika Anda ingin membatalkan serangkaian janji?
Matthieu Brucher
1
Masalah dengan pendekatan ini adalah Jika Anda memiliki Janji yang tidak akan pernah diselesaikan atau ditolak, janji itu tidak akan pernah dibatalkan.
DaNeSh
2
Ini sebagian benar, tetapi jika Anda memiliki rantai janji yang panjang, pendekatan ini tidak akan berhasil.
Veikko Karsikko
11

Saya sangat terkejut bahwa tidak ada yang menyebut Promise.racesebagai kandidat untuk ini:

const actualPromise = new Promise((resolve, reject) => { setTimeout(resolve, 10000) });
let cancel;
const cancelPromise = new Promise((resolve, reject) => {
    cancel = reject.bind(null, { canceled: true })
})

const cancelablePromise = Object.assign(Promise.race([actualPromise, cancelPromise]), { cancel });
Pho3nixHun
sumber
3
Saya tidak percaya ini berhasil. Jika Anda mengubah promise ke log, menjalankannya cancel()tetap akan mengakibatkan log dipanggil. `` const actualPromise = new Promise ((selesaikan, tolak) => {setTimeout (() => {console.log ('aktual dipanggil'); selesaikan ()}, 10000)}); ``
shmck
2
Pertanyaannya adalah bagaimana cara membatalkan sebuah promise (=> stop chained thens untuk dieksekusi), bukan bagaimana cara membatalkan setTimeout(=> clearTimeout) atau kode sinkron, di mana kecuali Anda meletakkan if setelah setiap baris ( if (canceled) return) ini tidak dapat dicapai. (Jangan lakukan ini)
Pho3nixHun
10
const makeCancelable = promise => {
    let rejectFn;

    const wrappedPromise = new Promise((resolve, reject) => {
        rejectFn = reject;

        Promise.resolve(promise)
            .then(resolve)
            .catch(reject);
    });

    wrappedPromise.cancel = () => {
        rejectFn({ canceled: true });
    };

    return wrappedPromise;
};

Pemakaian:

const cancelablePromise = makeCancelable(myPromise);
// ...
cancelablePromise.cancel();
Slava M
sumber
5

Sebenarnya tidak mungkin menghentikan pelaksanaan janji, tetapi Anda dapat membajak penolakan dan membatalkannya dari janji itu sendiri.

class CancelablePromise {
  constructor(executor) {
    let _reject = null;
    const cancelablePromise = new Promise((resolve, reject) => {
      _reject = reject;
      return executor(resolve, reject);
    });
    cancelablePromise.cancel = _reject;

    return cancelablePromise;
  }
}

Pemakaian:

const p = new CancelablePromise((resolve, reject) => {
  setTimeout(() => {
    console.log('resolved!');
    resolve();
  }, 2000);
})

p.catch(console.log);

setTimeout(() => {
  p.cancel(new Error('Messed up!'));
}, 1000);
nikksan
sumber
1
@dx_over_dt Hasil edit Anda akan menjadi komentar yang bagus, tapi bukan hasil edit. Mohon serahkan suntingan substantif tersebut ke lingkup OP (kecuali jika postingan tersebut ditandai sebagai Wiki Komunitas, tentu saja).
TylerH
@ TylerH jadi intinya mengedit untuk memperbaiki kesalahan ketik dan sejenisnya? Atau untuk memperbarui informasi karena sudah ketinggalan zaman? Saya baru dalam kemampuan mengedit hak istimewa postingan orang lain.
dx_over_dt
@dx_over_dt Ya, mengedit adalah untuk memperbaiki posting dengan memperbaiki kesalahan ketik, kesalahan tata bahasa, dan menambahkan penyorotan sintaks (jika seseorang hanya memposting banyak kode tetapi tidak membuat indentasi atau memberi tag dengan `` misalnya). Menambahkan konten substantif seperti penjelasan tambahan atau alasan / pembenaran untuk berbagai hal biasanya merupakan bidang orang yang memposting jawabannya. Anda bebas menyarankannya di komentar, dan OP akan diberi tahu tentang komentar tersebut dan kemudian dapat menanggapinya, atau mereka dapat memasukkan sendiri saran Anda ke dalam kiriman.
TylerH
@dx_over_dt Pengecualiannya adalah jika sebuah posting ditandai "Wiki Komunitas" yang menunjukkan bahwa itu dimaksudkan untuk berfungsi sebagai posting kolaboratif (misalnya seperti Wikipedia), atau jika ada masalah serius dengan posting seperti bahasa kasar / kasar, konten berbahaya / berbahaya ( mis. saran atau kode yang dapat menyebabkan Anda terkena virus atau membuat Anda ditangkap, dll.), atau info pribadi seperti catatan kesehatan, nomor telepon, kartu kredit, dll .; jangan ragu untuk menghapusnya sendiri.
TylerH
Perlu dicatat bahwa alasan eksekusi tidak dapat dihentikan dalam sebuah promise adalah karena JavaScript adalah single-threaded. Saat fungsi janji sedang dijalankan, tidak ada lagi yang berjalan, jadi tidak ada yang memicu penghentian eksekusi.
dx_over_dt
2

Inilah implementasi kami https://github.com/permettez-moi-de-construire/cancellable-promise

Digunakan seperti

const {
  cancellablePromise,
  CancelToken,
  CancelError
} = require('@permettezmoideconstruire/cancellable-promise')

const cancelToken = new CancelToken()

const initialPromise = SOMETHING_ASYNC()
const wrappedPromise = cancellablePromise(initialPromise, cancelToken)


// Somewhere, cancel the promise...
cancelToken.cancel()


//Then catch it
wrappedPromise
.then((res) => {
  //Actual, usual fulfill
})
.catch((err) => {
  if(err instanceOf CancelError) {
    //Handle cancel error
  }

  //Handle actual, usual error
})

yang mana:

  • Tidak menyentuh Promise API
  • Mari kita lakukan pembatalan lebih lanjut dalam catchpanggilan
  • Andalkan pembatalan ditolak bukannya diselesaikan tidak seperti proposal atau implementasi lainnya

Tarik dan komentar diterima

Cyril CHAPON
sumber
2

Janji dapat dibatalkan dengan bantuan AbortController.

Apakah ada metode untuk membersihkannya: ya Anda dapat menolak janji dengan AbortControllerobjek dan kemudian promiseakan melewati semua lalu memblokir dan langsung pergi ke blok tangkap.

Contoh:

import "abortcontroller-polyfill";

let controller = new window.AbortController();
let signal = controller.signal;
let elem = document.querySelector("#status")

let example = (signal) => {
    return new Promise((resolve, reject) => {
        let timeout = setTimeout(() => {
            elem.textContent = "Promise resolved";
            resolve("resolved")
        }, 2000);

        signal.addEventListener('abort', () => {
            elem.textContent = "Promise rejected";
            clearInterval(timeout);
            reject("Promise aborted")
        });
    });
}

function cancelPromise() {
    controller.abort()
    console.log(controller);
}

example(signal)
    .then(data => {
        console.log(data);
    })
    .catch(error => {
        console.log("Catch: ", error)
    });

document.getElementById('abort-btn').addEventListener('click', cancelPromise);

Html


    <button type="button" id="abort-btn" onclick="abort()">Abort</button>
    <div id="status"> </div>

Catatan: perlu menambahkan polyfill, tidak didukung di semua browser.

Contoh Langsung

Edit elegant-lake-5jnh3

Sohail
sumber
1

versi sederhana :

berikan saja fungsi tolak.

function Sleep(ms,cancel_holder) {

 return new Promise(function(resolve,reject){
  var done=false; 
  var t=setTimeout(function(){if(done)return;done=true;resolve();}, ms);
  cancel_holder.cancel=function(){if(done)return;done=true;if(t)clearTimeout(t);reject();} 
 })
}

solusi pembungkus (pabrik)

solusi yang saya temukan adalah dengan meneruskan objek cancel_holder. itu akan memiliki fungsi pembatalan. jika memiliki fungsi pembatalan maka ia dapat dibatalkan.

Fungsi pembatalan ini menolak janji dengan Kesalahan ('dibatalkan').

Sebelum menyelesaikan, menolak, atau on_cancel, cegah fungsi pembatalan dipanggil tanpa alasan.

Saya merasa nyaman untuk melewatkan tindakan pembatalan dengan suntikan

function cancelablePromise(cancel_holder,promise_fn,optional_external_cancel) {
  if(!cancel_holder)cancel_holder={};
  return new Promise( function(resolve,reject) {
    var canceled=false;
    var resolve2=function(){ if(canceled) return; canceled=true; delete cancel_holder.cancel; resolve.apply(this,arguments);}
    var reject2=function(){ if(canceled) return; canceled=true; delete cancel_holder.cancel; reject.apply(this,arguments);}
    var on_cancel={}
    cancel_holder.cancel=function(){
      if(canceled) return; canceled=true;

      delete cancel_holder.cancel;
      cancel_holder.canceled=true;

      if(on_cancel.cancel)on_cancel.cancel();
      if(optional_external_cancel)optional_external_cancel();

      reject(new Error('canceled'));
    };

    return promise_fn.call(this,resolve2,reject2,on_cancel);        
  });
}

function Sleep(ms,cancel_holder) {

 return cancelablePromise(cancel_holder,function(resolve,reject,oncacnel){

  var t=setTimeout(resolve, ms);
  oncacnel.cancel=function(){if(t)clearTimeout(t);}     

 })
}


let cancel_holder={};

// meanwhile in another place it can be canceled
setTimeout(function(){  if(cancel_holder.cancel)cancel_holder.cancel(); },500) 

Sleep(1000,cancel_holder).then(function() {
 console.log('sleept well');
}, function(e) {
 if(e.message!=='canceled') throw e;
 console.log('sleep interrupted')
})
Shimon Doodkin
sumber
1

Coba janji-abortable : https://www.npmjs.com/package/promise-abortable

$ npm install promise-abortable
import AbortablePromise from "promise-abortable";

const timeout = new AbortablePromise((resolve, reject, signal) => {
  setTimeout(reject, timeToLive, error);
  signal.onabort = resolve;
});

Promise.resolve(fn()).then(() => {
  timeout.abort();
});
Devi
sumber
1

Jika kode Anda ditempatkan di kelas, Anda dapat menggunakan dekorator untuk itu. Anda memiliki dekorator seperti itu di utils-decorators ( npm install --save utils-decorators). Ini akan membatalkan pemanggilan sebelumnya dari metode yang didekorasi jika sebelum penyelesaian panggilan sebelumnya ada panggilan lain untuk metode khusus itu.

import {cancelPrevious} from 'utils-decorators';

class SomeService {

   @cancelPrevious()
   doSomeAsync(): Promise<any> {
    ....
   }
}

https://github.com/vlio20/utils-decorators#cancelprevious-method

vlio20.dll
sumber
0

Jika Anda ingin menghentikan semua hasil tangkapan agar tidak dieksekusi, Anda dapat melakukan ini dengan memasukkan janji yang tidak akan pernah terpecahkan. Ini mungkin memiliki reprokusi kebocoran memori tetapi itu akan memperbaiki masalah dan seharusnya tidak menyebabkan terlalu banyak memori yang terbuang di sebagian besar aplikasi.

new Promise((resolve, reject) => {
    console.log('first chain link executed')
    resolve('daniel');
}).then(name => {
    console.log('second chain link executed')
    if (name === 'daniel') {
        // I don't want to continue the chain, return a new promise
        // that never calls its resolve function
        return new Promise((resolve, reject) => {
            console.log('unresolved promise executed')
        });
    }
}).then(() => console.log('last chain link executed'))

// VM492:2 first chain link executed
// VM492:5 second chain link executed
// VM492:8 unresolved promise executed
DanLatimer
sumber
0

Setel properti "dibatalkan" di Janji untuk memberi sinyal then()dan catch()keluar lebih awal. Ini sangat efektif, terutama di Web Worker yang memiliki tugas mikro yang ada yang mengantri di Promises from onmessagehandler.

// Queue task to resolve Promise after the end of this script
const promise = new Promise(resolve => setTimeout(resolve))

promise.then(_ => {
  if (promise.canceled) {
    log('Promise cancelled.  Exiting early...');
    return;
  }

  log('No cancelation signaled.  Continue...');
})

promise.canceled = true;

function log(msg) {
  document.body.innerHTML = msg;
}

AnthumChris
sumber
0

Jawaban @Michael Yagudaev berhasil untuk saya.

Tetapi jawaban asli tidak mengikat janji yang dibungkus dengan .catch () untuk menangani penanganan penolakan, berikut peningkatan saya di atas jawaban @Michael Yagudaev:

const makeCancelablePromise = promise => {
  let hasCanceled = false;
  const wrappedPromise = new Promise((resolve, reject) => {
    promise
      .then(val => (hasCanceled ? reject({ isCanceled: true }) : resolve(val)))
      .catch(
        error => (hasCanceled ? reject({ isCanceled: true }) : reject(error))
      );
  });

  return {
    promise: wrappedPromise,
    cancel() {
      hasCanceled = true;
    }
  };
};

// Example Usage:
const cancelablePromise = makeCancelable(
  new Promise((rs, rj) => {
    /*do something*/
  })
);
cancelablePromise.promise.then(() => console.log('resolved')).catch(err => {
  if (err.isCanceled) {
    console.log('Wrapped promise canceled');
    return;
  }
  console.log('Promise was not canceled but rejected due to errors: ', err);
});
cancelablePromise.cancel();

sumber
0

Jika p adalah variabel yang berisi Promise, maka p.then(empty);harus mengabaikan promise ketika akhirnya selesai atau jika sudah lengkap (ya, saya tahu ini bukan pertanyaan asli, tapi ini pertanyaan saya). "kosong" adalahfunction empty() {} . Saya hanya pemula dan mungkin salah, tetapi jawaban lain ini tampaknya terlalu rumit. Janji seharusnya sederhana.

David Spector
sumber
0

Saya masih mengerjakan ide ini, tetapi berikut adalah cara saya menerapkan janji yang dapat dibatalkan menggunakan setTimeout sebagai contoh.

Idenya adalah bahwa sebuah janji diselesaikan atau ditolak setiap kali Anda telah memutuskannya, jadi itu harus menjadi masalah memutuskan kapan Anda ingin membatalkan, memenuhi kriteria, dan kemudian memanggil reject()fungsi itu sendiri.

  • Pertama, saya pikir ada dua alasan untuk menyelesaikan janji lebih awal: untuk menyelesaikannya dan menyelesaikannya (yang saya sebut tekad ) dan untuk membatalkan (yang saya sebut menolak ). Tentu saja, itu hanya perasaan saya. Tentu saja ada Promise.resolve()metode, tapi itu dalam konstruktor itu sendiri, dan mengembalikan janji terselesaikan tiruan. resolve()Metode contoh ini sebenarnya menyelesaikan objek janji yang dibuat instance-nya.

  • Kedua, Anda dapat dengan senang hati menambahkan apa pun yang Anda suka ke objek promise yang baru dibuat sebelum Anda mengembalikannya, jadi saya baru saja menambahkan resolve()dan reject()metode untuk membuatnya mandiri.

  • Ketiga, triknya adalah dengan dapat mengakses eksekutor resolvedan rejectfungsi nanti, jadi saya hanya menyimpannya dalam objek sederhana dari dalam closure.

Saya pikir solusinya sederhana, dan saya tidak melihat ada masalah besar dengannya.

function wait(delay) {
  var promise;
  var timeOut;
  var executor={};
  promise=new Promise(function(resolve,reject) {
    console.log(`Started`);
    executor={resolve,reject};  //  Store the resolve and reject methods
    timeOut=setTimeout(function(){
      console.log(`Timed Out`);
      resolve();
    },delay);
  });
  //  Implement your own resolve methods,
  //  then access the stored methods
      promise.reject=function() {
        console.log(`Cancelled`);
        clearTimeout(timeOut);
        executor.reject();
      };
      promise.resolve=function() {
        console.log(`Finished`);
        clearTimeout(timeOut);
        executor.resolve();
      };
  return promise;
}

var promise;
document.querySelector('button#start').onclick=()=>{
  promise=wait(5000);
  promise
  .then(()=>console.log('I have finished'))
  .catch(()=>console.log('or not'));
};
document.querySelector('button#cancel').onclick=()=>{ promise.reject(); }
document.querySelector('button#finish').onclick=()=>{ promise.resolve(); }
<button id="start">Start</button>
<button id="cancel">Cancel</button>
<button id="finish">Finish</button>

Manngo
sumber